Skip to content

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.

llm-usage doctor [options]

Examples:

Terminal window
llm-usage doctor
llm-usage doctor --source pi --source codex
llm-usage doctor --claude-dir /path/to/.claude/projects
llm-usage doctor --json
  • --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
  • --quiet and --verbose: less or more on stderr

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.

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 installed

Searched 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 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"
}
]
}
}