Daemon troubleshooting
Most daemon issues fall into one of four buckets: the service won't start, the socket isn't reachable, events aren't appearing, or the database got corrupted. This page walks each.
:::tip Turn on debug logging first
Set OPENUSAGE_DEBUG=1 in your shell or in the launchd plist / systemd unit's Environment=. Verbose output in daemon.stderr.log (or journalctl --user-unit openusage-telemetry.service) is usually enough to diagnose the problem.
:::
Daemon won't start
Symptom
openusage telemetry daemon status reports the service is not running. launchctl print or systemctl --user status shows a failure.
Check the binary path
The plist or systemd unit captures the absolute path of openusage at install time. If you moved or replaced the binary, the service can't find it.
# macOS
launchctl print gui/$(id -u)/com.openusage.telemetryd | grep program
# Linux
systemctl --user cat openusage-telemetry.service | grep ExecStart
Fix:
openusage telemetry daemon uninstall
openusage telemetry daemon install
Check CGO
If the binary aborts immediately, you may have a non-CGO build. The daemon depends on mattn/go-sqlite3, which fails at runtime without CGO. Use the official release build, or compile with CGO_ENABLED=1.
macOS: re-load the plist
launchctl bootout gui/$(id -u)/com.openusage.telemetryd
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.openusage.telemetryd.plist
launchctl kickstart -k gui/$(id -u)/com.openusage.telemetryd
Linux: lingering disabled
If systemctl --user services don't survive logout, enable lingering once:
loginctl enable-linger $USER
Socket errors
Symptom
TUI shows "telemetry: not connected" or hooks log dial unix … connect: no such file or directory.
Check the socket
ls -la ~/.local/state/openusage/telemetry.sock
curl --unix-socket ~/.local/state/openusage/telemetry.sock http://localhost/healthz
A healthy daemon answers 200 OK. If the file is missing, the daemon isn't running. If it exists but /healthz hangs or refuses, the daemon is wedged — restart it.
Override paths drift
If you set --socket-path or OPENUSAGE_TELEMETRY_SOCKET for the daemon but not the TUI/hooks (or vice versa), they connect at different paths. Set the env var in your shell init so every process inherits it.
export OPENUSAGE_TELEMETRY_SOCKET=/tmp/openusage-telemetry.sock
Stale socket after crash
rm ~/.local/state/openusage/telemetry.sock
# then restart the service
The daemon recreates it on startup.
Inspecting logs
macOS
tail -f ~/.local/state/openusage/daemon.stderr.log
tail -f ~/.local/state/openusage/daemon.stdout.log
Linux
journalctl --user-unit openusage-telemetry.service -f
journalctl --user-unit openusage-telemetry.service --since "10 min ago"
The log files in ~/.local/state/openusage/ are also written on Linux when the unit redirects stdout/stderr.
Missing or duplicate events
Spool not draining
Files piling up in ~/.local/state/openusage/telemetry-spool/ indicate the daemon hasn't been able to ingest them.
Common causes:
- Daemon was offline when hooks fired — files will drain automatically once it's running.
- Persistent malformed payload — daemon logs will show parse errors. Move the offending file aside, restart, and investigate.
- DB was corrupt — fixed automatically (see below) but spool drain is paused until reinit completes.
Events show under the wrong provider
This is a provider link mismatch. Open the Telemetry settings tab (, then 6) and use m on the source row to pick the correct display provider, or edit telemetry.provider_links in settings.json.
Dedup ate a real event
The pipeline drops events whose dedup key matches an earlier row. If a tool re-uses tool_call_id across distinct events (an upstream bug), distinct turns can collapse into one. Workarounds:
- Upgrade the tool integration:
openusage integrations upgrade <id>. - Check
raw_eventsfor the dropped payload — it's still there even when the canonical row is deduped.
sqlite3 ~/.local/state/openusage/telemetry.db \
"SELECT id, source, schema, occurred_at FROM raw_events ORDER BY occurred_at DESC LIMIT 20;"
Database corruption
Symptom
Daemon log shows database disk image is malformed or file is not a database.
Automatic recovery
The daemon detects corruption on startup and:
- Renames the bad file to
telemetry.db.corrupt.{unix-ts}. - Removes orphaned
telemetry.db-shmandtelemetry.db-wal. - Initializes a fresh
telemetry.dband continues.
Look for the corrupt file:
ls ~/.local/state/openusage/telemetry.db.corrupt.*
You can attempt a recovery dump:
sqlite3 ~/.local/state/openusage/telemetry.db.corrupt.1715000000 \
".recover" > /tmp/recovered.sql
Once you're satisfied, delete the corrupt file.
Prevent future corruption
- Don't kill the daemon with
SIGKILLwhile a write is in flight. - Don't
cpthe live DB — usesqlite3 … .backup(see Storage). - Keep the state directory on a local disk; SQLite + WAL on networked filesystems is unreliable.
Reset everything
When you just want a clean start:
openusage telemetry daemon uninstall
rm -rf ~/.local/state/openusage/
openusage telemetry daemon install
This wipes the DB, spool, and logs. Hook scripts and tool config patches are unaffected (managed separately by openusage integrations).
Still stuck?
- Run the daemon in the foreground with verbose logging:
openusage telemetry daemon run --verbose. - Open an issue on GitHub with the relevant log excerpt and your platform.