Troubleshooting¶
Common issues¶
| Symptom | Cause | Fix |
|---|---|---|
| Just shows model name | No config file found | Create oh-my-line.json in your project root or ~/.oh-my-line/config.json |
| Rate limit segments empty | Not logged in via OAuth | Run claude and log in — the engine needs an OAuth session |
| Rate limit shows red ⚠ | Cannot connect to API or proxy | Check network, verify proxy URL, see debug log |
| Rate limit shows yellow ⚠ | Data is stale or proxy rate-limited | Data is still shown but may be outdated; will auto-recover |
| Rate limit shows "?" | API call failed or token expired | Check curl is installed; restart Claude Code to refresh OAuth |
| ETA segments not appearing | Not enough data yet | Wait 2min (short-term) or 10min (long-term) for warm-up |
| Command segment missing | Config is untrusted (project-level) | Command segments only run from trusted configs — see security |
| Command segment missing | Command errored or returned empty | Test manually: bash -c "your command" |
| Command segment stale | Cached output | Delete cache files (see below) or wait for cache TTL |
| Statusline blank/error | Invalid config file | Check JSON syntax with jq . < oh-my-line.json |
| Burn rate shows 0 | New context window just started | Wait for token consumption to begin |
| Statusline not updating | Claude Code not piping data | Restart Claude Code |
| Wrong project identity | Found a different config file | Check config lookup path (see below) |
| GitHub segments empty | Not in a git repo, no PR, or gh not installed |
Run gh auth status to check |
| GitHub segments show stale data | Cached response | Delete cache files (see below) |
Debugging config lookup¶
The engine checks for config files in this order:
# Check which config the engine would use
[ -f "$(pwd)/oh-my-line.json" ] && echo "Project: $(pwd)/oh-my-line.json"
[ -n "$CLAUDE_CONFIG_DIR" ] && [ -f "$CLAUDE_CONFIG_DIR/oh-my-line.json" ] && echo "Account: $CLAUDE_CONFIG_DIR/oh-my-line.json"
[ -f ~/.oh-my-line/config.json ] && echo "Global: ~/.oh-my-line/config.json"
Validating your config¶
# Check JSON syntax
jq . < oh-my-line.json
# Check structure (new format)
jq '."oh-my-lines" | length' < oh-my-line.json
# List configured segment types
jq '[ ."oh-my-lines"[].segments[]?.type ] | unique' < oh-my-line.json
Testing the engine directly¶
Pipe test data to the engine to verify it works:
echo '{"model":{"display_name":"Test Model"},"context_window":{"context_window_size":200000,"current_usage":{"input_tokens":50000,"cache_creation_input_tokens":0,"cache_read_input_tokens":0}},"cwd":"'$(pwd)'"}' | ~/.oh-my-line/oh-my-line
You should see your statusline rendered with ANSI colours.
Testing without colours¶
Strip ANSI codes to see raw text output:
Rate limit / OAuth issues¶
Rate limit segments (rate-session, rate-weekly, rate-extra, rate-opus) and ETA segments require an active OAuth session from Claude Code.
The engine resolves the OAuth token using this chain:
CLAUDE_CODE_OAUTH_TOKENenvironment variable- macOS Keychain (
security find-generic-password) - Credentials file (
{CLAUDE_CONFIG_DIR}/.credentials.json— defaults to~/.claude/.credentials.json) - GNOME Keyring (Linux, via
secret-tool)
In multi-account setups, the credentials file path is scoped to the account's CLAUDE_CONFIG_DIR, so each account resolves its own OAuth token.
If rate limits show nothing:
- Verify you're logged into Claude Code with an account (not just an API key)
- Check that
curlis installed - Try restarting Claude Code to refresh the OAuth session
Usage proxy issues¶
If you've configured a usage proxy and rate-limit segments show ⚠ indicators:
-
Red ⚠ (unreachable) — the proxy URL cannot be reached
- Verify the proxy is running:
curl -s YOUR_PROXY_URL | jq . - Check the URL is correct in your config or env var
- Enable debug logging to see the exact error:
export OML_DEBUG=1
- Verify the proxy is running:
-
Yellow ⚠ (stale) — the proxy is responding but data is old
- The proxy may be rate-limited upstream (check
meta.rate_limitedin response) - Data is still displayed — it's just not the latest
- The proxy may be rate-limited upstream (check
-
Clear stale state — delete cached files to force a fresh fetch:
Debug logging¶
Enable debug logging to troubleshoot rate limits, proxy connections, OAuth, and cache behavior:
Or set in config:
Logs are written to ~/.oh-my-line/debug.log. Check the log after a statusline render:
The log includes timestamps, component tags, and key=value data for each event. Auto-truncates at 100KB.
Temp files¶
The engine writes transient state to /tmp/claude-{uid}/ (created with ownership and symlink checks).
For multi-account setups (using CLAUDE_CONFIG_DIR), account-scoped files are stored in subdirectories:
/tmp/claude-{uid}/ ← default account (shared + account-scoped)
/tmp/claude-{uid}/acct-{hash}/ ← additional account (account-scoped only)
Shared files (always in base directory):
| File | Purpose | TTL |
|---|---|---|
statusline-version.txt |
Cached Claude Code version string | 1hr |
statusline-cmd-*.dat |
Cached command segment outputs | Per config |
statusline-gh-*.json |
Cached GitHub CLI responses | 60s-5min |
statusline-docker.json |
Cached Docker container status | 30s |
Account-scoped files (in base dir for default account, acct-{hash}/ for others):
| File | Purpose | TTL |
|---|---|---|
statusline-usage-cache.json |
Cached API/proxy response for rate limits | 5min |
statusline-usage-cache.json.tmp |
In-progress background fetch result | Transient |
statusline-usage-cache.json.pid |
PID of background fetch process | Transient |
statusline-usage-cache.json.err |
Connection error marker | 5min |
statusline-burn.dat |
Burn rate tracking (start epoch + tokens) | Session |
statusline-eta-short.dat |
Short-term ETA reference point | ~5min |
statusline-eta-long.dat |
Long-term ETA reference point | ~60min |
statusline-spark-*.dat |
Sparkline bucket data | Session |
Debug log (separate location):
| File | Purpose |
|---|---|
~/.oh-my-line/debug.log |
Debug log (when OML_DEBUG=1 or "debug": true) — auto-truncated at 100KB |
To find your cache directory:
# Default account
ls /tmp/claude-$(id -u)/statusline-* 2>/dev/null
# All accounts (including multi-account subdirs)
ls /tmp/claude-$(id -u)/acct-*/statusline-* 2>/dev/null
To reset all cached state:
# Default account only
rm -f /tmp/claude-$(id -u)/statusline-*
# All accounts
rm -rf /tmp/claude-$(id -u)/acct-*
How the statusline refreshes¶
The statusline is not on a fixed timer. Claude Code calls the engine on every prompt submission and response — the statusCommandHook pipes fresh context data (model, tokens, cwd) to the engine each time. If you stop interacting with Claude Code, the statusline stays on its last rendered value.
Rate limit data works differently — it's too expensive to fetch from the API on every render. Instead:
- The first render that needs rate limits launches a background
curlsubprocess - The subprocess writes its result to a cache file (TTL: 5 minutes)
- Subsequent renders read from cache instantly
- When the cache expires, the next render launches a fresh background fetch
- While the new fetch is in-flight (~1-2 seconds), the old cached data is shown with a yellow ⚠ indicator
This means rate limit data may lag by a few seconds after a cache expiry, but it self-corrects without any user action.
Why do I see a yellow ⚠ on rate limits?¶
The yellow ⚠ means the engine is showing stale cached data while a background refresh is in progress. This is normal and resolves itself within a few seconds.
You'll typically see it:
- When you first open a session (no cache yet, first fetch in progress)
- After 5 minutes of inactivity followed by a new prompt (cache expired)
- When switching between projects (different cache contexts)
No action needed — the indicator disappears once the background fetch completes. If it persists for more than 30 seconds, check your network connection or see Debug logging.
A red ⚠ is different — it means the API or proxy is unreachable. See Usage proxy issues for details.
Security: trusted vs untrusted configs¶
The engine has three config paths with different trust levels:
| Path | Trusted | command segments |
|---|---|---|
{cwd}/oh-my-line.json |
No | Silently blocked |
{CLAUDE_CONFIG_DIR}/oh-my-line.json |
Yes | Executed |
~/.oh-my-line/config.json |
Yes | Executed |
Why? command segments run arbitrary shell commands (bash -c "..."). A cloned repo could include a malicious oh-my-line.json that executes code every time the statusline renders. Trusted configs — the global config and per-account configs — are safe because the user explicitly controls them.
Project-level configs work for everything else — layout, segments, styling, product identity. Only shell command execution is restricted.
If your command segments aren't appearing, check which config the engine is loading — if it's a project-level oh-my-line.json, move the command segments to a trusted config (~/.oh-my-line/config.json or your per-account config).
Multi-account issues¶
If you run multiple Claude Code accounts via CLAUDE_CONFIG_DIR, each account gets isolated cache, OAuth, and cost tracking automatically. See Multi-account setup for details.
| Symptom | Cause | Fix |
|---|---|---|
| Rate limits from wrong account | Cache cross-contamination (pre-v0.11) | Clear all cache: rm -rf /tmp/claude-$(id -u)/statusline-* /tmp/claude-$(id -u)/acct-* |
| Effort level wrong in second account | Reading default ~/.claude/settings.json |
Upgrade to v0.11+ — effort is now read from account's config dir |
| Cost data mixed between accounts | Shared cost directory (pre-v0.11) | Upgrade to v0.11+ — cost dirs are now per-account |
To check which account key the engine derived:
# The engine logs the account key in debug mode
OML_DEBUG=1 echo '{"model":{"display_name":"Test"},...}' | ~/.oh-my-line/oh-my-line 2>&1
tail -5 ~/.oh-my-line/debug.log
To list per-account cache directories:
Running the test suite¶
Getting help¶
- GitHub Issues — report bugs or request features
- Builder — visual config builder