Fullscreen renderer auto-disables itself on machines running many concurrent sessions (false boot-canary strikes)
What happens
I run a lot of Claude Code sessions concurrently on one machine (10-20 at a time, launched in a burst and torn down together). Since the fullscreen boot canary landed in 2.1.236, fullscreen keeps turning itself off:
Claude Code's fullscreen renderer didn't finish starting last time on this machine, so this launch is using the classic renderer. It will try fullscreen again next launch; /tui default keeps the classic renderer.
and then, shortly after:
Claude Code's fullscreen renderer has repeatedly failed to start on this machine, so it has been turned off here. Run /tui fullscreen to try it again (this also resets after an update).
I have "tui": "fullscreen" in ~/.claude/settings.json. Fullscreen itself works fine, it renders perfectly every time. Nothing is actually failing to start. But I have to run /tui fullscreen again every few minutes, which is maddening.
Why I don't think these are real failures
fullscreenAutoDisabled in ~/.claude.json came back as:
{"version": "2.1.251", "at": 1788146545484, "strikes": 4}
The sticky threshold is 2, so a genuine "failed twice in a row" should trip at exactly 2. Landing on 4 means several strikes were banked from a single scan, which looks like a burst of orphaned fullscreenBootPending entries rather than four separate failed launches.
That map is keyed by PID and lives in the single shared ~/.claude.json. Every concurrent session adds its entry at startup and removes it ~10s later. When many sessions start and settle at nearly the same instant, they're all doing read-modify-write against the same file, and any lost update leaves an entry behind for a PID that is by then gone. On the next launch that reads as a crashed boot.
Two things make it hard to recover from:
- Strikes are only cleared on a
healthysettle. A signal-driven exit settles aswithdrawn, which removes that PID's own entry but deliberately preserves the strike counter. On a machine where sessions are usually terminated rather than quit interactively, the counter effectively only ratchets up. - PID reuse is a false positive by construction: an entry whose PID matches the launching process's own PID is counted as a strike immediately, without checking whether it's actually the same process. With this much process churn, PID recycling is routine.
Repro shape
- Set
"tui": "fullscreen"in~/.claude/settings.json. - Launch ~15 sessions concurrently from a supervising process, each in its own pty.
- Let them run past the 10s healthy mark, then terminate the whole group.
- Relaunch. Watch
fullscreenBootStrikes/fullscreenAutoDisabledin~/.claude.json.
What I tried
I assumed the supervisor was SIGKILLing sessions before cleanup finished, so I widened the SIGTERM-to-SIGKILL grace period. It didn't help, and if anything it got worse, which is consistent with contention rather than truncation: a longer grace means more processes alive and writing to the same file at the same moment.
Workaround
CLAUDE_CODE_NO_FLICKER=1 in the environment. That makes the entry path env_on, which is excluded from the set that arms the canary, so nothing is written and nothing can be orphaned. Fullscreen then just stays on.
Suggestions
- Make the pending-map update atomic across processes, or tolerate lost updates by not treating an unattributable leftover entry as a strike.
- Don't count
pid === ownPidas a strike without confirming it's the same process (comparestartedAtagainst the current process start time). - Cap strikes to at most one per launch, so one burst of orphans can't bank several at once.
- Clear the strike counter on a
withdrawnsettle too, or at least on any settle where the renderer demonstrably reached its first frame.
Environment
- Claude Code 2.1.251 (native install)
- macOS 15.x (Darwin 25.6.0), arm64