Daemon issues
The daemon is a background service that polls providers and accepts hook posts. When it misbehaves, the symptoms usually fall into one of the categories below.
Daemon won't start
Symptoms: openusage telemetry daemon status reports not running, or the install command exits non-zero.
Cause: installing from go run
openusage telemetry daemon install writes a launchd plist (macOS) or systemd unit (Linux) that points at the binary's current path. If you're running via go run, that path is a temporary build directory that disappears after the command exits.
Cannot install from go run (transient binary).
Fix: install a permanent binary first.
make build
sudo install -m 0755 bin/openusage /usr/local/bin/openusage
openusage telemetry daemon install
Or use the release tarball / Homebrew formula.
Cause: service file already exists
Reinstalling can fail if a stale plist/unit is in place. Uninstall first:
openusage telemetry daemon uninstall
openusage telemetry daemon install
Cause: socket directory not writable
The daemon creates ~/.local/state/openusage/ if missing. If ~/.local/ exists but is not writable by your user, creation fails. Check:
ls -ld ~/.local ~/.local/state ~/.local/state/openusage 2>/dev/null
Fix permissions with chown / chmod or pick a different state dir via XDG_STATE_HOME.
Dashboard stuck on "upgrade telemetry daemon service"
Symptoms: the startup screen shows something like
✗ upgrade telemetry daemon service: launchctl bootstrap user/501 ... failed:
exit status 5 (Bootstrap failed: 5: Input/output error)
and the dashboard never loads.
Cause: two OpenUsage versions fighting over one service
Every dashboard talks to the same background service (com.openusage.telemetryd on macOS). Older dashboards (0.25.x and earlier) reinstall the service whenever its version differs from their own. If you run a new build while an older dashboard is still open in another terminal or tmux pane, each one keeps replacing the other's service. A launchctl bootstrap that lands while the other process has just loaded the service fails with error 5.
From the release after 0.25.1, the dashboard:
- waits for launchd to finish unloading the old service and retries the bootstrap on error 5 or 37,
- serialises reinstalls across OpenUsage processes with a lock file (
~/.local/state/openusage/daemon.install.lock), - upgrades the service automatically at most once per process and never downgrades it,
- keeps going with the helper that is running and shows a yellow warning instead of stopping on the startup screen.
Old dashboards that are still running don't have these fixes, so they can still reinstall their own version.
Fix:
# 1. Find every running OpenUsage dashboard and the binary it runs from
ps -axo pid,lstart,command | grep '[o]penusage'
# 2. Quit the old ones (q in their pane, or kill <pid>), or upgrade them:
brew upgrade openusage
# 3. Reinstall the service from the binary you want to keep
openusage telemetry daemon install # or ./bin/openusage telemetry daemon install
openusage telemetry daemon status # "Overall compatibility: yes"
openusage telemetry daemon status shows both sides. Service program is the binary the installed plist or unit launches, Executable (this binary) is the binary you just ran, and Daemon version is what is actually answering on the socket. If Service program changes back after you install, another OpenUsage process reinstalled the service. openusage telemetry daemon install now checks the helper's version after installing and reports this case instead of claiming success. Provider registry ... (compatible: no) means the helper was built with a different set of providers.
Socket errors (EACCES, ECONNREFUSED)
Symptoms: TUI shows "daemon not reachable" or hooks log socket errors.
ECONNREFUSED
The socket file exists but nothing is listening. Usually means the daemon crashed.
openusage telemetry daemon status
# macOS
launchctl print gui/$(id -u)/com.openusage.telemetryd
# Linux
systemctl --user status openusage-telemetry.service
If the service is dead, restart it:
# macOS
launchctl kickstart -k gui/$(id -u)/com.openusage.telemetryd
# Linux
systemctl --user restart openusage-telemetry.service
EACCES
The socket file exists but the current user can't connect. This happens when:
- Two users share the host and one daemon owns the socket.
- A previous run wrote with different permissions.
Fix: each user runs their own daemon with their own socket. To force a different path:
export OPENUSAGE_TELEMETRY_SOCKET=$HOME/.local/state/openusage/telemetry.sock
Socket path mismatch
Both the daemon and the TUI default to ~/.local/state/openusage/telemetry.sock. If you set --socket-path on one but not the other, they don't meet. Use OPENUSAGE_TELEMETRY_SOCKET to set both.
Log inspection
Logs are written in two places.
Files
~/.local/state/openusage/daemon.stdout.log
~/.local/state/openusage/daemon.stderr.log
Tail them while reproducing the issue:
tail -f ~/.local/state/openusage/daemon.stderr.log
journald (Linux)
journalctl --user-unit openusage-telemetry.service -f
Verbose mode
If symptoms only appear under load, enable verbose logging:
openusage telemetry daemon uninstall
openusage telemetry daemon run --verbose # foreground, prints to terminal
Reproduce the issue, then reinstall when done.
SQLite corruption
Symptoms: daemon logs show database disk image is malformed or cannot open database.
Auto-recovery
The daemon does this for you on startup: it renames the corrupt file to telemetry.db.corrupt.<timestamp>, removes any stale -shm / -wal files, and creates a fresh database. You'll lose history beyond what's still in the spool, but the service comes back up.
Manual recovery
If the auto-recovery doesn't fire (e.g. corruption appears mid-run), stop the daemon and clear the files:
# macOS
launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.openusage.telemetryd.plist
# Linux
systemctl --user stop openusage-telemetry.service
# Move the files aside (don't delete in case forensics are needed)
mv ~/.local/state/openusage/telemetry.db ~/.local/state/openusage/telemetry.db.bak
rm -f ~/.local/state/openusage/telemetry.db-shm ~/.local/state/openusage/telemetry.db-wal
# Restart
openusage telemetry daemon install # if uninstalled
# or kickstart (macOS) / restart (Linux) as above
Preventing it
The store is configured with WAL and synchronous=NORMAL. Corruption is rare absent disk failure or a hard kill mid-write, but NORMAL doesn't fsync on every transaction — a sudden power loss can lose the most recent in-flight events. Avoid forcing the host to power off while a poll is in flight.
Hooks not delivering events
Symptoms: integration is installed but new events from the agent don't appear in the dashboard.
-
Confirm the hook is installed and current:
openusage integrations listoutdatedmeans the on-disk template lags behind the binary's bundled version. Runopenusage integrations upgrade --all. -
Confirm the daemon is running (see above).
-
Check the spool. If the daemon was down, hooks should have spooled. After the daemon comes back, events drain on the next interval.
ls ~/.local/state/openusage/telemetry-spool/ -
Re-install the hook. Backup files are written to
.baknext to the originals, so this is non-destructive:openusage integrations install claude_code
Resetting everything
Last-resort wipe:
openusage telemetry daemon uninstall
rm -rf ~/.local/state/openusage
openusage telemetry daemon install
You'll lose all history. Auto-detection rebuilds account configuration on the next TUI launch.