Correct what a switch actually does to a running session

Every one of these said a switch leaves running sessions alone - that they keep
talking to the old provider until restarted. That is wrong, and reassuring in
exactly the wrong direction: a switch breaks them.

The static half of the config does behave as described. Base URL, model ids and
the env block are read once at startup and a running session keeps what it
started with.

The credential is not. It comes from running apiKeyHelper, which Claude Code
re-invokes on a timer - CLAUDE_CODE_API_KEY_HELPER_TTL_MS is present in the
2.1.251 binary - and claude-key-helper.sh answers for whatever state.json says at
that moment. So the switch reaches into a live session through the one part that
was never cached. Into anthropic, the helper returns nothing by design and the
next refresh comes back with no credential; into another provider, it hands over
the new key while the session is still pointed at the old base URL, which
rejects it. Either way the session starts failing calls whenever the TTL happens
to expire, mid-turn as easily as between turns.

One case does survive, and is now stated rather than glossed: switching between
two presets of the same provider that share a keyRef keeps the same key and the
same endpoint, so the session carries on with the model ids it started with.

The claim in "Restarting sessions" predates this work - it came in with the
Windows build - but it is wrong for the same reason and is corrected too.
This commit is contained in:
smoido
2026-08-30 21:41:09 +03:00
parent 22cadaebc5
commit 9c301e10a1
3 changed files with 70 additions and 27 deletions
+15 -8
View File
@@ -48,10 +48,12 @@ Panel {
// ---- Switch flow
//
// A switch is not one action. It rewrites settings.json, which every future
// session reads and no running session re-reads, so two things have to be
// settled before the write: whether the target can actually serve requests,
// and what should happen to the sessions still talking to the old one.
// A switch is not one action. It rewrites settings.json, and two things have
// to be settled before the write: whether the target can actually serve
// requests, and what should happen to the sessions already running - which a
// switch breaks rather than leaves alone, because their credential is
// re-fetched on a timer and resolves to the new mode while their endpoint
// stays the old one.
//
// list -> preflight -> blocked
// -> sessions -> confirm -> switch -> list
@@ -848,10 +850,15 @@ Panel {
font.bold: true
}
// Not "they will keep using the old provider". Their endpoint and model
// ids are fixed at startup, but the key is re-fetched on a timer and
// will resolve to the new mode, which the old endpoint refuses - so
// they fail rather than carry on.
Text {
width: parent.width
text: "Claude Code reads its settings once, at startup, so these keep "
+ Modes.title(root.mode) + " until they are restarted."
text: "They keep pointing at " + Modes.title(root.mode) + ", but their key is "
+ "re-fetched on a timer and will switch under them. Their calls start "
+ "failing from that moment, not at a clean stop."
color: Color.muted
wrapMode: Text.WordWrap
lineHeight: 1.2
@@ -863,8 +870,8 @@ Panel {
width: parent.width
visible: parent.busyCount > 0
text: parent.busyCount === 1
? "One is working right now and will lose that turn."
: parent.busyCount + " are working right now and will lose those turns."
? "One is working right now and will break mid-turn."
: parent.busyCount + " are working right now and will break mid-turn."
color: root.urgentColor
wrapMode: Text.WordWrap
font.family: root.bar ? root.bar.fontFamily : Style.font.family