[BUG] One failed statusLine invocation erases the last rendered text — bar goes blank with no error (statusLineText set to undefined)
Summary
When a custom statusLine command fails once — non-zero exit, spawn failure, or empty stdout — Claude Code does not keep the previously rendered text. It actively clears the internal statusLineText to undefined, rendering a blank status bar with no error surfaced anywhere. On a loaded machine (several parallel agent sessions → occasional transient spawn failures), long-lived sessions flicker between a rendered bar and a blank one.
This is the intermittent/transient variant of #76380 (persistent failure → permanently blank). Root cause below covers both.
Environment
- Claude Code 2.1.223 (build
2026-08-05T18:12:31Z, git sha4535f697); same code present in 2.1.222 - macOS (darwin arm64, Darwin 25.6.0), Ghostty 1.3.1
- Config:
"statusLine": { "type": "command", "command": "python3 ~/.claude/scripts/statusline.py" }
Root cause (from bundle inspection of 2.1.223)
The command executor (minified jda) returns undefined for every failure mode:
async function jda(e, t, r) {
...
let s = await oJo(n, "StatusLine", ...);
if (s.aborted) return; // undefined
...
if (s.status === 0) {
let l = s.stdout.trim()....join("\n");
if (l) { ...; return l } // only success path
}
...
return; // nonzero exit / spawn failure / empty stdout → undefined
} catch { return } // exec error → undefined
The result handler (minified mHT) forwards that undefined into the store unconditionally — only the abort-signal path preserves the old text:
let l = await r();
if (t.aborted) return; // abort keeps stale text…
i(l); // …but a failure commits `undefined`
onResult: (Y) => { setState((X) => X.statusLineText === Y ? X : { ...X, statusLineText: Y }) }
Render branch: a ? <text> : showSpace() ? " " : null → blank row. No error reaches the UI; stderr only goes to debug logging.
So a single transient failure erases the last good status text. It stays blank until the next successful run (next message-driven refresh tick, a statusLine.command string change, or the refreshInterval timer if configured).
Evidence it's not the user command
I wrapped the configured command in a logging shim that records START/END/exit-code/duration per invocation. The underlying script always exits 0 in ~100–200 ms and never prints empty output (it has a catch-all that prints a fallback line). Blank episodes correlate with invocation-level failures under load, not with script behavior. Also reproducible reasoning-wise via #76380's scenario (exit 127 → permanent blank).
Expected
Stale-on-error: a failed refresh should keep the last successfully rendered text (optionally with a warning in debug logs). Clearing the bar should be reserved for deliberate unconfiguration. Concretely, in mHT: if (l === undefined) return; — the same treatment the abort path already gets.
Actual
statusLineText is set to undefined → blank bar, silently.
Related observation
statusLine.refreshInterval (seconds; Math.max(1, K) * 1000 → interval timer) exists in 2.1.223 and works — "refreshInterval": 5 makes blanks self-heal within 5 s and hot-reloads without restart. It appears to be undocumented on https://code.claude.com/docs/en/statusline; documenting it would give users a mitigation, though stale-on-error is the real fix.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗