-
Notifications
You must be signed in to change notification settings - Fork 0
Troubleshooting
Start with the smallest failing layer: service → control/state → ASR → refinement → output → HUD.
systemctl --user status voice-input.service voice-input-hud.service
systemctl --user cat voice-input.service voice-input-hud.service
journalctl --user -u voice-input.service -u voice-input-hud.service -b
voice-input status --format json --extendedThe daemon should create:
$XDG_RUNTIME_DIR/voice-input/control.sock
$XDG_RUNTIME_DIR/voice-input/state.json
$XDG_RUNTIME_DIR/voice-input/waveform.sock
Check them without printing state contents:
stat "$XDG_RUNTIME_DIR/voice-input/control.sock" \
"$XDG_RUNTIME_DIR/voice-input/state.json" \
"$XDG_RUNTIME_DIR/voice-input/waveform.sock"Common causes:
-
failed to connect to daemon: the daemon is stopped, crashed during config/credential loading, or the CLI and service use differentXDG_RUNTIME_DIRvalues. -
Config parse error: run
voice-input config; compare enum spelling with Configuration. -
Binary/assets mismatch after update: rerun
make enable-service, then inspect the rendered unit paths. Cargo downloads any newly locked dependencies as needed. - Hyprland unavailable to the service: make sure the systemd user-manager environment contains the graphical session variables, then restart the daemon.
A manually launched daemon and the systemd daemon must not compete for the same socket. Stop one before starting the other.
Settings requires Quickshell 0.3 or newer. Start with non-secret executable and asset checks:
/usr/bin/qs --version
test -x "$(command -v voice-input)"
test -d ~/.local/share/voice-input/quickshell-settings
pgrep -af 'qs.*voice-input/quickshell-settings'Running voice-input settings a second time should activate the existing regular window through Quickshell IPC, not create a duplicate. If no instance exists, the launcher uses /usr/bin/qs --daemonize --no-duplicate --path …/quickshell-settings. To expose QML startup diagnostics in a terminal, first close the Settings window, then run the same config in the foreground:
VOICE_INPUT_BIN="$(readlink -f "$(command -v voice-input)")" \
/usr/bin/qs --no-duplicate --path \
"$HOME/.local/share/voice-input/quickshell-settings"QML starts a dedicated hidden backend. In troubleshooting or development, verify only its protocol envelope without displaying config or credential data (requires jq):
printf '%s\n' \
'{"version":1,"id":1,"method":"settings.get","params":{}}' \
| voice-input settings-backend --stdio \
| jq '{version, id, ok, error: (.error.code // null)}'Expected output has version: 1, the same id, and ok: true. Do not send credential replacement requests from a shell: entered secrets are intended to travel from password fields through inherited stdin without entering shell history.
For save failures, validate without printing config and check permissions:
voice-input config >/dev/null
stat -c '%a %n' "$HOME/.config/voice-input" \
"$HOME/.config/voice-input/config.toml"
systemctl --user status voice-input.serviceExpected modes are 700 for the directory and 600 for the file. A revision-conflict message means the exact source changed after Settings loaded it; reload and reapply the intended edit. If Settings says persistence succeeded but restart failed, the new file remains saved—diagnose voice-input.service separately with systemctl and journalctl.
The standard deployment uses a separate Quickshell service. Restarting only voice-input.service does not restart the HUD:
systemctl --user restart voice-input-hud.service
journalctl --user -u voice-input-hud.service -fCheck /usr/bin/qs and the installed QML:
test -x /usr/bin/qs
ls -l ~/.local/share/voice-input/quickshell/Then distinguish the two HUD inputs:
-
state.jsonchanges phase/transcript. Test withvoice-input status --follow --format json --extended. -
waveform.sockcarries 30-bar NDJSON frames. The QML client retries after 400 ms.
If status changes but no HUD appears, focus on Quickshell/QML, focused-monitor detection, and voice-input-hud.service. If the HUD phase changes but bars remain flat, focus on waveform.sock, microphone PCM, and Qwen speech activity. The waveform is intentionally hidden when realtime Server VAD does not mark voice active.
The resident Quickshell HUD reads all geometry controls from the snapshot. Inspect only those non-transcript fields:
jq '{hud_enabled, hud_margin_bottom, hud_height,
hud_position, hud_offset_x, hud_offset_y}' \
"$XDG_RUNTIME_DIR/voice-input/state.json"If hud_enabled is false, the surface stays hidden although voice-input-hud.service remains resident. Margin, height, position, and offsets are no longer fixed in QML.
Remove stale runtime sockets only while the daemon is stopped; startup normally removes/rebinds its own control and waveform sockets:
systemctl --user stop voice-input.service
rm -f "$XDG_RUNTIME_DIR/voice-input/control.sock" \
"$XDG_RUNTIME_DIR/voice-input/waveform.sock"
systemctl --user start voice-input.serviceRealtime sessions deliberately cancel when no server speech event or non-empty transcript arrives within a 350 ms grace period at stop. The log says:
voice-input realtime ASR: no speech detected; cancelling empty dictation
No final pass, LLM request, or output occurs in this case.
Check:
pw-record --raw --rate 16000 --channels 1 --format s16 /tmp/voice-input-test.raw
# Speak briefly, stop with Ctrl+C, confirm the file is non-empty, then delete it.
rm -f /tmp/voice-input-test.rawAlso verify audio.device, Qwen connectivity, turn_mode, vad_threshold, and silence_duration_ms. Adjust VAD values incrementally according to the provider's accepted range.
The current cancellation gate runs before a manual-mode stop commit. A manual session that has produced no prior transcription event can therefore be treated as silent. Prefer server-vad when diagnosing this behavior. Local CLI sessions do not use the realtime no-speech gate.
voice-input record cancel and an empty captured buffer also return to idle without output by design.
Both dedicated capture and shared pre-roll capture stop automatically at audio.max_duration_secs, which defaults to 300 seconds. The journal reports:
voice-input capture: reached configured 300-second limit; finishing automatically
This is a normal stop: final ASR, local fallback, refinement, and output still run according to configuration. Increase the setting only after considering the larger in-memory PCM buffer and final-pass upload.
Realtime audio uses a bounded, nonblocking queue. If recent local speech continues for eight seconds without a Qwen transcription event, Voice Input reconstructs the realtime session once and replays all retained PCM from the beginning. The HUD reports reconnecting and buffered-audio replay while capture, waveform delivery, and elapsed time continue. The journal records both attempt numbers, the interruption reason, and retained packet/sample counts without logging audio content.
If the replacement also stalls, or if either WebSocket closes prematurely, Voice Input enters full-audio recovery. Queue overflow and a disconnected worker remain separate terminal conditions, reported as audio queue could not keep up or ASR worker disconnected. On stop, Voice Input rejects the incomplete remote transcript and uses the complete recording through final ASR or local fallback. If neither recovery path is enabled, finalization reports that no full-audio recovery is available; enable final_pass_enabled or fallback_to_local for resilient long recordings.
The elapsed clock and a fallback state refresh are driven by the same visible animation heartbeat as the halo. If a moving halo is ever accompanied by frozen text or time, capture the daemon and HUD journals together; that combination should no longer depend solely on QML timer scheduling.
Follow the daemon journal while recording:
journalctl --user -u voice-input.service -fCheck:
-
provider = "alibaba-qwen-realtime"and a validalibaba-api-keycredential; - endpoint region, model ID, and network/DNS/TLS access;
- connect/finalize timeouts;
-
/usr/bin/voxtypeiffallback_to_local = true; - final model/base URL/timeout if
final_pass_enabled = true.
An empty final-pass base URL is inferred only for known dashscope.aliyuncs.com, dashscope-intl.aliyuncs.com, and dashscope-us.aliyuncs.com realtime hosts. Set it explicitly for a custom host.
Realtime partial text remains usable when the initial stream completes normally. A first transcript stall triggers one reconstruction; the replacement transcript becomes authoritative after complete buffered-audio replay. If that replacement fails, either connection closes, the worker disconnects, or queue backpressure occurs, realtime processing is incomplete, so its transcript is rejected and only a full-audio recovery path is eligible. A failed final pass then falls back to local CLI when enabled. The journal records attempt transitions, worker termination errors, and WebSocket close codes/reasons without printing transcript content.
test -x /usr/bin/voxtype
/usr/bin/voxtype --helpVoice Input supports backend setup only through these CLI forms, which proxy to the configured backend:
voice-input setup gpu [backend arguments...]
voice-input setup onnx [backend arguments...]For recognition, it invokes the configured executable with engine/model/language flags and transcribe <temporary-wav>. Confirm the selected engine/model is installed in Voxtype itself. Never set backend_command = "voice-input"; that would recurse into this program.
Both local and remote Chinese final text invoke:
opencc -c t2s # simplified-chinese
opencc -c s2t # traditional-chineseCheck availability:
command -v opencc
printf '測試\n' | opencc -c t2sIf opencc cannot be launched, recognition returns an error. If it launches but exits nonzero or emits empty output, Voice Input retains the unconverted transcript. English, Japanese, and Korean bypass OpenCC.
voice-input llm test
journalctl --user -u voice-input.service -fConfirm enabled = true, a non-empty model, the correct api_base_url, and openrouter-api-key. The contextual attempt and any eligible no-context retry share one deadline. timeout_ms defaults to 15 seconds and is clamped to 1–30 seconds. With a budget of at least 10 seconds, the contextual attempt leaves the final five seconds available for transcript-only recovery.
Refinement deliberately keeps ASR text on all failures. Context-free retry is eligible after transport errors (including timeout), recognized payload/context errors, invalid response structure, truncation, or contextual budget exhaustion when at least one second remains. Non-retryable HTTP responses such as authentication and rate-limit failures, and provider-declared errors, keep the original ASR text immediately.
Text over 120 characters automatically uses paste even when mode = "type". XWayland also prefers paste by default.
Verify the route and tools:
command -v wtype wl-copy wl-paste
command -v xclip xdotool # XWayland only
journalctl --user -u voice-input.service -b | grep 'voice-input output:'The logs identify target, mode, and driver without printing text. Check these cases:
-
mode = "clipboard"copies only; it never sends a paste chord. - The target application may not accept
shift+Insert; setpaste_keysorxwayland_paste_keysto its supported chord, such asctrl+v. - A clipboard manager or application may change ownership during the 220 ms restore delay.
- Unsupported clipboard MIME types or a failed backup can prevent complete restoration.
- If modifier keys are still held, raise
pre_type_delay_ms; toggle+modifier shortcuts already enforce at least 500 ms. - To test direct typing for XWayland, temporarily set
prefer_paste_for_xwayland = false, understanding that some clients garble directwtypeinput.
Use voice-input status --format json --extended to inspect the last resolved output_target, output_mode, and output_driver (the JSON field is output_target_resolved).
List metadata only; do not decrypt a key into a shared terminal or report:
ls -l ~/.config/credstore.encrypted/
systemctl --user cat voice-input.service
journalctl --user -u voice-input.service -bThe blob names must exactly match alibaba-api-key and openrouter-api-key. Settings offers keep/replace only: a blank password field keeps the current blob, while a non-empty field replaces it and is cleared after submission. Test LLM uses an entered LLM key when present and otherwise uses the encrypted-store credential. After editing credentials:
systemctl --user restart voice-input.serviceEncrypted credentials are supplied only to the systemd-launched service. A manual voice-input daemon has no $CREDENTIALS_DIRECTORY unless launched in an appropriate credential context; it can use the documented environment fallback, with the associated exposure risk.
If Settings cannot encrypt/decrypt, verify systemd-creds exists and the user's systemd credential mechanism works. Do not place keys into public TOML examples or issue logs.
Restart Pi or reload its extensions. The installed TypeScript file is not loaded retroactively into an existing Pi process:
ls -l ~/.pi/agent/extensions/voice-input-session-registry.ts
ls -l "$XDG_RUNTIME_DIR/voice-input/agent-sessions/"If no registry appears after Pi restart, inspect Pi extension loading. If a registry exists, verify that Pi is the focused foreground process in a Kitty window and that the Kitty endpoint is exactly /tmp/kitty-<kitty-pid>. See Agent Context for the full validation chain.
Codex does not use this extension; it must have exactly one eligible CLI session JSONL open under CODEX_HOME/sessions.
Use the structured timing lines instead of dumping state or session files:
journalctl --user -u voice-input.service -b \
| grep -E 'first partial|session finished|voice-input refinement:|voice-input output:'These lines normally contain elapsed times, counts, outcomes, and drivers rather than transcript/context. Provider/backend errors can still contain external diagnostics. Read and redact the output before sharing; never attach state.json, Pi/Codex JSONL, raw audio, credentials, or a complete unreviewed journal.
See also: Security and Privacy · Development
English Home · 简体中文首页 · Source repository · MIT License
Voice Input is an independent community project. HUD and Settings require Quickshell 0.3+. Review Security and Privacy before enabling remote ASR, LLM refinement, pre-roll, agent context, or replacing credentials in Settings.
Voice Input 是独立的社区项目。HUD 和 Settings 需要 Quickshell 0.3 或更高版本。启用远程 ASR、LLM refinement、pre-roll、Agent 上下文,或者在 Settings 中替换 credential 前,请阅读安全与隐私。