Troubleshooting & Debugging¶
In a nutshell
You said something to OVOS and nothing happened. This page is a decision tree for finding out why. It follows the same journey as The Life of an Utterance: mic, wake word, speech-to-text, intent matching, skill, text-to-speech. At each stop it shows where the evidence lives (which log file, which bus message), what a healthy result looks like, and the exact command to check it yourself. The first stages need only copy-paste: no programming background is required there. Later stages read log files and run command-line tools, so they ask more of you as the page goes. New to the words here? See the Glossary.
Every stage below can be checked two ways: tail a log file (works everywhere, including headless
boxes over SSH), or watch the bus live with ovos-busmon (works anywhere a browser can reach the
device, and shows every stage in one place instead of six log files). Start with the logs. They
need no extra install. Then reach for ovos-busmon when you want everything in one filterable view.
The debugging funnel¶
flowchart TD
S1{"Stage 1: bus reachable?\nbus.log"}
S1 -- no --> F1["start ovos-messagebus,\nthen ovos-core / ovos-dinkum-listener"]
S1 -- yes --> S2{"Stage 2: mic/wake word fired?\nrecognizer_loop:record_begin"}
S2 -- no --> F2["check mic device/gain\nor wake-word plugin"]
S2 -- yes --> S3{"Stage 3: STT produced text?\nrecognizer_loop:utterance"}
S3 -- no --> F3["check STT plugin / network / API key"]
S3 -- yes --> S4{"Stage 4: intent matched?\novos.intent.matched"}
S4 -- no --> F4["vocabulary/training data\nin the target skill"]
S4 -- yes --> S5{"Stage 5: handler raised?\novos.intent.handler.error"}
S5 -- error --> F5["read the traceback\nin skills.log"]
S5 -- clean --> S6{"Stage 6: TTS spoke?\novos.utterance.speak"}
S6 -- no --> F6["check TTS plugin / audio sink"]
S6 -- yes --> S7{"Stage 7: media played?\novos.common_play.query / .status"}
S7 -- no --> F7["check OCP skills / stream extractor / audio sink"]
S7 -- yes --> Done["utterance handled correctly"]
Diagram: The flow starts at Stage 1, checking whether the bus is reachable, and ends at "utterance handled correctly," and it branches at each of the seven stages to a fix action whenever that stage's check fails. Stage 7 only applies to media/playback requests ("play some jazz"); a plain spoken answer ends at Stage 6.
Each stage below cites the exact log line and bus message shown in this diagram, plus the command to check it directly.
This is where it turns technical
From here on, the page reads log files and runs command-line tools. If that is more than you want to deal with, try everyday-help.md instead — it covers common problems with no terminal needed.
Where the logs live¶
OVOS runs several independent services (listener, intent/skills, audio, messagebus, GUI), and each one writes its own log file, named after the service. By default they land under the XDG state directory. On a typical Linux install that is:
~/.local/state/mycroft/
├── audio.log # ovos-audio — TTS + playback
├── bus.log # ovos-messagebus
├── skills.log # ovos-core — intent matching + skill execution
├── voice.log # ovos-dinkum-listener — mic, wake word, STT
└── ovos.log # any process that never set its own service name (see note)
Where ovos.log comes from
The shared logger (ovos-utils) names its log file after whatever service name was set.
If a process never sets one, such as a one-off script, a plugin running standalone, or a
service started before it calls its own name-setting step, it falls back to the logger's
own default name, OVOS, lower-cased to ovos.log. Seeing this file usually just means
some component is logging under the generic default rather than its own service log.
The directory can be overridden per-service via the logs.path config key (or logging.<service>.
logs.path for a per-service override). See Turning up log detail below.
Each service also logs to stdout, so if it runs under systemd or Docker, journalctl /
docker logs shows the same lines.
ovos-logs (shipped by ovos-utils) is the quickest way to read them without hunting for the path:
ovos-logs show -l skills # page through skills.log with `less`
ovos-logs list --error # every ERROR line across all logs
ovos-logs slice --start "1-1-2024 09:00" # a time-bounded slice
See the Command-line Tools page for the full flag reference.
Watch the bus while you speak: ovos-busmon¶
All the stages in this guide talk to each other over the messagebus: the listener
emits an utterance message, the intent service emits a match, the skill emits a speak, and so on.
ovos-busmon is a small web app that connects to
the bus as a client and streams every one of those messages live to a browser tab, in one
filterable, searchable timeline, letting you follow a single utterance across every stage below
instead of tailing six log files.
What to check: install with pip install ovos-busmon, run ovos-busmon, and open
http://127.0.0.1:8005. Filter by recognizer_loop:* to see the raw utterance leave the
listener, ovos.intent.matched / ovos.utterance.handled to see which pipeline stage claimed
it, and ovos.utterance.speak to see whether TTS fired. Keep it bound to 127.0.0.1/your LAN,
never expose it to the internet: it mirrors every message on the bus, including STT transcripts.
Authentication is off by default; a non-loopback bind refuses to start until you set
BUSMON_TOKEN or BUSMON_USERNAME/BUSMON_PASSWORD.
See Watching the Bus: ovos-busmon for the full install/config
reference, the security warning in detail, and a step-by-step walkthrough.
Stage 1: Is the service even running, and is the bus reachable?¶
Log: bus.log. Bus visual: any message appearing at all in ovos-busmon
Before anything else, confirm the messagebus server is up: everything else in OVOS is a client of it, so if it is down nothing downstream can work.
ovos-listen is the simplest possible probe. It emits mycroft.mic.listen and exits.
What a working bus looks like: the command returns to the shell within a second or so, and the device starts listening.
What an unreachable bus looks like depends on your version:
-
ovos-bus-client2.7.3 and later print the address that was tried and exit non-zero: -
Earlier versions hang indefinitely and print nothing at all (ovos-bus-client#274). A terminal that just sits there is the symptom. Press
Ctrl+Cand readbus.log, or check the service directly withsystemctl --user status ovos-messagebus.service. Do not read the silence as success.
Either way, start (or restart) ovos-messagebus, then ovos-core and ovos-dinkum-listener,
and check bus.log for a clean startup (no repeated Connection Refused lines). Clients
reconnect on their own with a backing-off retry (5 s → 60 s cap), so a bus restart does not
require restarting every client by hand. See Bus restart / reconnect
behavior for exactly what to expect while they
recover.
In ovos-busmon, this stage is trivially visible: if the bus was down when busmon started, its
connection never came up (busmon does not auto-retry) and the stream stays empty. Restart busmon
once the bus is back.
Nothing wrong with the mic yet
This stage says nothing about audio hardware. It only confirms the messagebus itself accepts connections. Hardware problems show up in Stage 2.
Prove the microphone and speaker work¶
Still copy-paste territory
The checks in this section are plain copy-paste commands with the expected result described after each one. You do not need to read logs or understand the output. The more technical material starts after this section.
Do these checks before you read wake word or STT logs. They test the sound card itself, not OVOS, and they work on any Linux install (Raspberry Pi image or otherwise). If you're on raspOVOS, there is also a Pi-specific diagnostics script: see RaspOVOS Troubleshooting, "Audio Issues".
Does the OS see your microphone and speaker?¶
Run this command:
It lists every capture device the kernel found. Look for your microphone's card. If the list is
empty, the OS does not see the microphone. Unplug the microphone, plug it into a different USB
port, and run arecord -l again. If the list stays empty, the problem is the microphone, its
cable, or a missing driver, not OVOS. Ask for help in the
OVOS Matrix chat or the
Open Conversational AI forum with the output of
arecord -l and your microphone model.
List playback devices the same way:
If your speaker is missing from this list, OVOS cannot play audio, no matter how you configure the software.
Record and play back a test file¶
Run this command:
It records 5 seconds of audio, then plays the recording back. Speak during the recording. If you hear your own voice, the mic and speaker both work at the hardware level. If you hear silence or noise only, the problem is the microphone, its gain, or its wiring, not OVOS.
Check capture level and mute¶
Run this command:
Press F4 to switch to the capture view. Check the capture level is not near zero and not
muted. An MM mark means muted; press m to toggle it. Press Esc to exit.
You can also read and set levels without the interactive UI:
PulseAudio or PipeWire?¶
Most current Linux systems route audio through PipeWire. Some still use PulseAudio. Either one sits between the raw ALSA device and OVOS, and either can mute or misroute a device that ALSA itself sees fine.
- PipeWire: run
wpctl statusto list sinks (speakers) and sources (microphones). The default device is marked with*. Inspect one device withwpctl inspect $ID. - PulseAudio: run
pactl list short sourcesfor microphones, andpactl list short sinksfor speakers.
If a device works at the ALSA level (the arecord/aplay checks above) but OVOS still can't
use it, check here that it is not muted or set to the wrong default.
Stage 2: Did the mic/wake word fire?¶
Log: voice.log (service ovos-dinkum-listener). Bus filter: recognizer_loop:record_begin
/ record_end, ovos.listener.record.started / ovos.listener.record.ended
Before reading voice.log, confirm the hardware itself works: see Prove the microphone and
speaker work above.
A healthy wake-word trigger and recording cycle looks like this in voice.log:
DEBUG - Record begin
DEBUG - Hotword utterance: hey mycroft
DEBUG - Emitting hotword event: recognizer_loop:wakeword
...
DEBUG - Record end
Common failure signatures:
Symptom in voice.log |
Likely cause |
|---|---|
No Record begin line at all when you speak |
Wake-word plugin isn't hearing you: check the mic device/gain, or the wake word model/sensitivity. |
Record begin fires but never followed by Record end |
VAD never detects silence: check the VAD plugin config. |
| Repeated wake-word triggers with no speech after | False positives: the wake-word threshold may be too low. |
To hear the recorded audio yourself, turn on the listener's own recording keys in
configuration (user config, under "listener"):
{
"listener": {
"save_utterances": true,
"record_wake_words": true,
"save_path": "/tmp/ovos-audio-debug"
}
}
save_utterances writes every STT-bound recording as a .wav file under
<save_path>/utterances/. record_wake_words does the same for wake-word triggers under
<save_path>/wake_words/. Set them with:
These files are raw recordings of everything the microphone picked up, and they stay on disk
until you delete them. Nothing prunes the directory. Turn both keys back off and clear
<save_path> once you have finished debugging. See
Privacy & Security for what the listener writes
to disk by default.
ovos-listen can also force a listening cycle without saying the wake word at all. This is
useful for isolating STT problems (Stage 3) from wake word problems.
Stage 3: Did STT produce text?¶
Log: voice.log. Bus filter: recognizer_loop:utterance (spec name ovos.utterance.handle)
Once recording stops, the audio is handed to the STT plugin. A healthy transcription shows up as:
Common failure signatures:
| Symptom | Likely cause |
|---|---|
ERROR - Empty transcription, either recorded silence or STT failed! |
The STT engine returned nothing: check the STT plugin is installed/configured and (for online engines) that the network/API key is working. |
INFO - Ignoring low confidence STT transcriptions: [...] |
A confidence filter dropped the candidate: check min_stt_confidence in the listener config. |
| Transcription text is garbled/wrong words | STT engine or language mismatch, not a bug in the pipeline: try a different STT plugin or model size. |
To skip the microphone and STT entirely and test everything downstream of this point, inject the text directly onto the bus as if STT had already produced it:
This runs MessageBusClient().emit(Message("recognizer_loop:utterance", {"utterances": ["what time
is it"], "lang": "en-US"})), exactly the same message the listener would have emitted. It is the
single most useful command for isolating "is my problem in audio, or in matching/skills?" without
having to speak into a microphone at all.
In ovos-busmon, filter by recognizer_loop:* (or the spec name ovos.utterance.handle): seeing
the utterance land there with the right text confirms STT worked, regardless of what happens next.
Stage 4: Which pipeline stage matched (or didn't)?¶
Log: skills.log (or intents.log if the intent service runs standalone). Bus filter:
ovos.intent.matched, ovos.utterance.handled
ovos-core's IntentService logs every step of matching. A healthy match looks like:
INFO - Parsing utterance: ['what time is it']
INFO - adapt_high match (en-us): IntentMatch(...)
DEBUG - final intent match: {...}
adapt_high here is one entry of the configurable intents.pipeline list. The bundled default
pipeline (see Pipelines Overview) uses the canonical plugin IDs, in this
order:
ovos-stop-pipeline-plugin-high, ovos-converse-pipeline-plugin, ovos-ocp-pipeline-plugin-high,
ovos-padatious-pipeline-plugin-high, ovos-adapt-pipeline-plugin-high, ovos-m2v-pipeline-high,
ovos-ocp-pipeline-plugin-medium, ovos-fallback-pipeline-plugin-high,
ovos-stop-pipeline-plugin-medium, ovos-padatious-pipeline-plugin-medium,
ovos-adapt-pipeline-plugin-medium,
ovos-fallback-pipeline-plugin-medium, ovos-fallback-pipeline-plugin-low
If nothing matches, every matcher in that list logs a
miss (as no match from <bound method ...>, naming the matcher's Python function) before the
utterance falls through to the next stage, and eventually to nothing:
DEBUG - no match from <bound method ...StopService.match_high ...>
DEBUG - no match from <bound method ...ConverseService.match ...>
DEBUG - no match from <bound method ...OCPPipelineMatcher.match_high ...>
DEBUG - no match from <bound method ...PadatiousPipeline.match_high ...>
DEBUG - no match from <bound method ...AdaptPipeline.match_high ...>
...
Use ovos-say-to (Stage 3) to reproduce this deterministically without speaking, then grep
skills.log for the exact utterance text:
Every request ends with exactly one ovos.utterance.handled event, whether an intent matched or
not. Its absence means the intent service itself crashed or hung — jump back to
Stage 1's systemctl --user status check to confirm
ovos-core is even alive before digging further. Its presence with no matched
intent means every pipeline plugin genuinely rejected the utterance.
"Unknown pipeline matcher" in the logs. A bare pip install ovos-core ships with no
pipeline matcher plugins at all — by design, so a minimal deployment isn't forced to pull in
every intent engine. If intents.pipeline in mycroft.conf names a matcher (e.g.
adapt_high, ocp_high) whose plugin package (ovos-adapt-pipeline-plugin,
ovos-ocp-pipeline-plugin, …) isn't installed, the intent service logs "Unknown pipeline
matcher" for it and skips it. Install the matcher plugin explicitly, or use the installer,
which bundles a working default set — or, for a dev/test environment, pip install
"ovos-core[plugins]" pulls in Adapt, Padatious, Padacioso and the M2V/OCP/common-query
matchers together. Padatious itself carries no LGPL dependency (it's pure NumPy, Apache-2.0
licensed) and lives in this same [plugins] extra, not a separate one.
Before blaming the skill's vocabulary, rule out the two upstream causes of "installed but never matches":
- Did the skill load at all? A skill that failed to load registers no intents, so it
can never match — check
skills.logforFailed to load skill: <skill_id>/Load of skill <skill_id> failed!(a traceback follows), or watch for themycroft.skills.loading_failurebus event; every healthy load announcesmycroft.skills.loaded(plural) instead — plugin-skill loads via the Skill Manager also emit a singularmycroft.skill.loaded. See Skill Manager. Note the connectivity/RuntimeRequirementsgate is off by default — on a stock install every installed skill loads unconditionally, so don't chase network-requirement theories unlessuse_deferred_loadingwas deliberately enabled. - Has the matcher retrained since the install? Registering intents fires an
asynchronous
mycroft.skills.train, so a skill installed seconds ago may simply not be trained yet. The Padatious pipeline answers withmycroft.skills.trainedwhen its training round finishes — that is the deterministic signal to watch inovos-busmon(other pipeline plugins, like Adapt, don't need training and emit nothing).
Only after both check out is a persistent no-match usually a vocabulary/training-data problem
in the target skill, not a bug. See Intent Layers and the
Pipelines Overview for how to add or reorder matchers. And if the
user reports a beep rather than silence, that is the sounds.error earcon confirming a
complete intent failure — see Audio feedback cues.
In ovos-busmon, filter by ovos.intent.matched (see which skill/intent name claimed it) or by
ovos.utterance.handled (confirm the lifecycle closed at all). This reproduces the same
information as the log grep above but across the whole pipeline at a glance, and lets you inspect
the full JSON payload of the match.
Stage 5: Did the skill handler raise?¶
Log: skills.log. Bus filter: ovos.intent.handler.error (legacy mycroft.skill.handler.
error). Part of the handler-lifecycle trio ...handler.start → ...complete / ...error
described in The Life of an Utterance
Once a skill's intent handler is invoked, any unhandled exception inside it is caught by the skill base class, logged, and (unless disabled) spoken back as a generic error dialog:
and the corresponding ...error bus message is emitted so the orchestrator knows the handler
failed rather than silently returning nothing. Common failure signatures to grep for in
skills.log:
| Symptom | Likely cause |
|---|---|
A traceback right after final intent match |
The skill's own handler code raised: read the traceback, it names the exact file/line. |
Failed to update settings.json |
Non-fatal. A settings write failed after a successful handler run. It doesn't explain a silent utterance. |
No traceback, no speak, handler simply never runs |
The bus dispatch itself failed to reach the skill process: check the skill actually loaded (see Skill Manager) and its skill_id matches the matched intent. |
In ovos-busmon, filter by *handler.error to catch any handler-lifecycle error message across
every skill in one view, or filter by the specific skill's ID to isolate its traffic.
Stage 6: Did TTS speak?¶
Log: audio.log (service ovos-audio). Bus filter: ovos.utterance.speak (legacy
speak), ovos.utterance.handled
Once a skill calls self.speak(), ovos-audio picks up the message. A healthy synthesis + playback
cycle logs:
Common failure signatures:
Symptom in audio.log |
Likely cause |
|---|---|
EXCEPTION - TTS synth failed! ... |
The TTS plugin itself errored (model missing, API failure for an online engine, bad voice config). |
ERROR - No fallback TTS available and main TTS failed! |
Both the primary and fallback TTS engines failed: check both are configured, or that the fallback exists at all. |
No Speak: line at all, despite the skill having matched |
The ovos.utterance.speak message never reached ovos-audio: check ovos-audio is actually running (Stage 1) and not crashed. |
Speak: line present but no sound |
Not a bug in the pipeline: check the audio sink: ALSA/PulseAudio device selection, system volume, or that the WAV file was actually written and playable. |
To test synthesis in isolation, without going through STT or intent matching at all:
This emits a bare speak message directly, exercising exactly the dialog-transformer → TTS plugin →
tts-transformer → playback path described in The Life of an Utterance.
If this command produces sound but a real skill interaction doesn't, the problem is upstream (Stages
1-5), not in audio output.
In ovos-busmon, filter by ovos.utterance.speak. Present with no sound points at the audio sink.
Absent, with everything upstream present, points at a silent or failed skill handler (Stage 5).
Stage 7: Media and playback¶
Log: audio.log (service ovos-audio, or ovos-media if enabled). Bus filter:
ovos.common_play.query / ovos.common_play.query.response, ovos.common_play.status
This stage only applies to media requests ("play some jazz", "next song"), not plain spoken
answers. It starts once Stage 4 has already
matched the utterance to the OCP pipeline (ovos-ocp-pipeline-plugin-high /
-medium / -low). If OCP never claims the utterance at all, that is a Stage 4 problem, not a
Stage 7 one: check the pipeline miss log described there first.
What to check: three failure shapes, in order. (a) Search found nothing: is an OCP-enabled
media skill installed at all (see Media Skills (OCP))? (b) A result was found
but the stream never starts: is the right stream extractor plugin (e.g.
ovos-ocp-youtube-plugin) installed for that source? © Audio plays on the wrong output or not
at all: run the hardware/mixer checks from Prove the microphone and speaker
work above, and check the OCP backend's
preferred_audio_services order.
See Troubleshooting Media and Playback for the full breakdown of
all three cases, the exact log lines and ovos-busmon filters for each, and links to the
relevant OCP config pages.
Turning up log detail¶
By default every service logs at INFO. To see the DEBUG lines quoted throughout this page
(pipeline misses, hotword events, STT confidence filtering, etc.), raise the log level in the
user configuration.
ovos-config set only edits keys that already exist somewhere
ovos-config set -k log_level -v DEBUG looks for log_level in the currently merged
configuration first, and on a fresh install nothing ships that key by default. So the
command fails with Error: No key that fits the query before you've ever set a log level.
The reliable first-time path is to add the key directly to your user config file
(~/.config/mycroft/mycroft.conf, creating it if it doesn't exist yet):
Once the key exists anywhere in the merged configuration (including after you've added it
this way once), ovos-config set -k log_level -v DEBUG will find it and write it to
mycroft.conf (the User layer). If mycroft.conf was already showing the wrong value
before running set, on an ovos-config release before 3.1.1a1, set wrote to the
Assistant layer (runtime.conf) instead — see Configuration —
and the User layer's stale value silently won.
This applies to every service (they all watch the same configuration and pick up the change
without a restart). To raise the level for only one service, add the nested "logging" section
instead. The same first-time caveat applies, so add it directly to the user config file:
log_level only takes effect from user or system configuration
This key is deliberately not honored in the bundled defaults or the remote/backend configuration layer. It must be set locally (user or system config) to take effect. See Configuration for how the configuration layers are merged.
Two environment variables set the starting level and logger name before any configuration loads,
mostly useful when running a service by hand: OVOS_DEFAULT_LOG_LEVEL and OVOS_DEFAULT_LOG_NAME.
OVOS_CONFIG_BASE_FOLDER sets the XDG base folder name (default mycroft) that logging and
PIDLock use to find the config and PID directories.
Reproducing an issue offline with ovoscope¶
If a bug reproduces reliably, don't keep re-triggering it on real hardware. Capture it once and
replay it. ovoscope runs an in-process, mocked assistant (MiniCroft)
that loads real skills and the real intent-matching engines without any audio hardware, and its CLI
can turn a live bus session into a fixture file:
# capture a fixture; --live records from a running OVOS instance
ovoscope record --utterance "what time is it" --output fixture.json --live
# replay a fixture and exit non-zero on failure
ovoscope run fixture.json
# compare two fixture files
ovoscope diff expected.json actual.json
record also takes --skill-id to choose which skills load, --lang (default en-US),
--pipeline to restrict the stages, and --bus-url for a non-default bus address.
This turns "it happens sometimes on the device but I can't tell why" into a fixed, replayable test
case. See the ovoscope guide for the full workflow, including
End2EndTest for writing an assertion once the fixture is captured.
Where to ask for help¶
If the logs and bus traffic don't explain the problem, the OpenVoiceOS community is active on:
- OVOS Chat on Matrix, real-time chat with maintainers and other users. The skills channel is specific to skill development questions.
- Open Conversational AI forum. Longer-form questions, bug reports, and searchable past discussions.
When asking for help, include the relevant log excerpt (or an ovos-busmon JSON export) for the
stage where you stop seeing the expected messages. It is almost always faster to diagnose with the actual message
sequence than with a description of the symptom alone.
Read next: The Life of an Utterance Related: Command-line Tools · Configuration · ovoscope Overview · Bus Service · RaspOVOS Troubleshooting · Watching the Bus: ovos-busmon · Troubleshooting Media and Playback