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
+26 -9
View File
@@ -300,9 +300,27 @@ If the profile doesn't load: `Set-ExecutionPolicy -Scope CurrentUser RemoteSigne
## Restarting sessions
Claude Code reads all of this **once at startup**. A switch does not affect a
running session — that's how the process loads config, not something scripting
can change.
A switch **breaks** running sessions. This originally said it did not affect
them; that was wrong, and the difference matters, because the two halves of the
config behave differently.
The static half — base URL, model ids, the env block — genuinely is 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`, present in 2.1.251),
and the helper answers for whatever `state.json` says *at that moment*. So a
switch reaches into a live session through the one thing that was never cached:
| switching to | what the running session gets |
|---|---|
| `anthropic` | the helper returns nothing, by design — no credential at all |
| another provider | the new provider's key, sent to the old base URL, which rejects it |
| another preset of the *same* provider, same `keyRef` | same key, same endpoint — this one survives, on the model ids it started with |
Either of the first two starts failing calls whenever the TTL happens to expire:
mid-turn as easily as between turns. So restart afterwards — and on Linux,
`claude-mode sessions --restart` will do it for you:
- **CLI** — exit and relaunch `claude`
- **VS Code** — `Ctrl+Shift+P`*Developer: Reload Window*
@@ -446,12 +464,11 @@ stays exactly as it was.
## Sessions still on the old provider
Claude Code reads all of this once, at startup. A switch therefore does nothing
to a session already running: it keeps talking to the old provider on the old
key until it is restarted, which is the confusing part — the bar says one thing
and the session in front of you is doing another. A session mid-request is worse
off, because anthropic mode *deletes* the helper, so an in-flight turn can fail
on its next tool call rather than stopping cleanly.
A switch does not leave running sessions on the old provider — it breaks them.
See [Restarting sessions](#restarting-sessions) for the mechanism: their
endpoint is fixed at startup but their credential is re-fetched on a timer, so
it switches under them and the endpoint they are still pointed at refuses it.
Which is why this is a decision rather than a notification.
```
claude-mode sessions