Doctor
llm-usage doctor answers the question “why does a source show no usage?” For each source it lists the paths it searched, how many files it found, and whether those files contain readable usage. It does not load pricing or write to the ledger.
Command structure
Section titled “Command structure”llm-usage doctor [options]Examples:
llm-usage doctorllm-usage doctor --source pi --source codexllm-usage doctor --claude-dir /path/to/.claude/projectsllm-usage doctor --jsonOptions
Section titled “Options”--source <name>: probe only the named sources (repeatable or comma-separated)- source-path overrides (
--pi-dir,--codex-dir,--opencode-db,--source-dir, …): probe a custom location --json: emit the result as JSON--quietand--verbose: less or more onstderr
doctor reads the same user config as the reports, so configured sourceDirs and eventStore paths are probed and its resolved config is printed on stderr.
Output
Section titled “Output”Doctor prints one line per source (a state glyph, the source id, its storage format jsonl, json, or sqlite, and what was found), followed by the paths it searched, then a summary. Each source is in one of four states:
| Glyph | State | Meaning |
|---|---|---|
✔ |
found |
Files exist and contain readable usage. |
○ |
not_installed |
No files in any searched path. The tool is not installed, has no sessions yet, or stores them elsewhere; pass a path override. |
⚠ |
unparseable |
Files exist, but the newest ones (up to 25) yield no usage. The tool may have changed its log format, or its sessions record no token usage (see the source guide). |
✖ |
error |
Discovery failed, for example an explicit directory override that does not exist. |
To decide between found and unparseable, doctor parses the newest files first and stops at the first one with usage, so it stays fast for file-per-session sources. A SQLite source (OpenCode, Goose, Antigravity) holds its history in one database, which doctor reads whole. It exits 0 in every state, so it is safe to run in scripts.
Illustrative output for llm-usage doctor --source pi,codex,opencode (counts depend on your local data):
✔ pi jsonl 519 files ~/.pi/agent/sessions ~/.omp/agent/sessions○ codex jsonl not installed (no files found) ~/.codex/sessions✖ opencode sqlite OpenCode DB path is missing or unreadable: /path/to/opencode.db /path/to/opencode.db✔ event-store sqlite 159,266 events, 3 departed files, schema v5, 78.4 MiB
Sources: 1 found · 1 failed · 1 not installedSearched paths reflect overrides, config sourceDirs, and tool environment variables such as CLAUDE_CONFIG_DIR, CODEX_HOME, and GEMINI_CLI_HOME. The summary counts only source rows; the event-store row reports the ledger’s event count (usage events, tool calls and messages), departed-file count, schema version, and on-disk size.
Each machine added with machine add gets a machine:<name> row (format ssh) from its cached usage: when it last synced, how many files and events are cached, and the last error. Doctor never connects to it; a row whose last sync failed is marked ✖. Without any machine, doctor ends with a tip on adding one.
JSON shape
Section titled “JSON shape”--json returns a sources array under data. status says whether discovery ran (ok or error); source rows add state (above) and searchedPaths. Rows whose discovery ran carry itemsFound, unparseable rows and the event-store row a detail string, and failed rows error:
{ "schemaVersion": 1, "report": "doctor", "data": { "sources": [ { "id": "pi", "format": "jsonl", "status": "ok", "itemsFound": 519, "state": "found", "searchedPaths": ["/home/me/.pi/agent/sessions", "/home/me/.omp/agent/sessions"] }, { "id": "opencode", "format": "sqlite", "status": "error", "state": "error", "error": "OpenCode DB path is missing or unreadable: /path/to/opencode.db", "searchedPaths": ["/path/to/opencode.db"] }, { "id": "event-store", "format": "sqlite", "status": "ok", "itemsFound": 159266, "detail": "159,266 events, 3 departed files, schema v5, 78.4 MiB" } ] }}