(withdrawn)

Resolved 💬 1 comment Opened Aug 17, 2026 by jimmylba Closed Aug 18, 2026

Withdrawn.

View original on GitHub ↗

1 Comment

jdcodes1 · 10 days ago

Traced the resume half on main @ 1f41cc5d92 — the "accepted with no error, silently ignored" behavior is explicit in code, and your WS-reconnect repro lands on it deterministically.

The mechanism. thread/resume first probes whether the thread is still loaded in the app-server. For a loaded thread, request overrides are checked by collect_resume_override_mismatches, which hard-codes exactly your case:

https://github.com/openai/codex/blob/1f41cc5d92722748e45cae9cecc6d883a4e7cbb1/codex-rs/app-server/src/request_processors/thread_processor.rs#L150-L152

("config overrides were provided and ignored while running"). What happens next (#L3862-L3903): if the loaded thread is idle, unsubscribed, and not running, it's torn down and the resume proceeds cold — overrides then apply. But if any subscriber is still registered (or shutdown fails/times out), the code keeps "rejoin semantics": the overrides are dropped with only a server-side tracing::warn! — the RPC succeeds, nothing in the response indicates anything was ignored. After a WebSocket reconnect, the previous connection's subscription is exactly the kind of thing that can still be registered when the new thread/resume arrives, which makes your scenario hit the ignore path consistently rather than racily.

So the observed split is: thread/start → fresh config load, overlay honored; thread/resume shortly after reconnect → loaded-thread rejoin, overlay warned-and-dropped where only the server log can see it. (thread/fork needs the same audit — it derives the child from the loaded parent's state, and your result suggests it inherits the parent's config rather than re-applying the request overlay.)

Fix shapes:

  1. Make the drop visible: return the mismatch list in the resume/fork response (or a warning notification) instead of tracing::warn!. Your report demonstrates why this matters — hosts minting short-TTL MCP bearer tokens can't even detect that their re-assertion was discarded.
  2. Honor the overlay: when config is present, either force the cold-resume path (tear down the loaded thread even with lingering subscribers — arguably correct after a reconnect anyway, since the subscriber is dead), or apply the overlay to the live session through the existing config-refresh machinery (refresh_mcp_config exists for exactly this kind of live mutation).
  3. One durability caveat for whoever implements (2): MCP desired-state is rebuilt on every runtime refresh (auth ticks, config changes — see the refresh pipeline discussed in #38925). A request-scoped mcp_servers overlay must survive those rebuilds, or the tools will appear at resume and silently vanish at the first refresh — your +3s/+20s/+40s probe methodology would catch this, and it's worth encoding as the regression test: resume with overlay → assert tools present after a forced runtime refresh, not just immediately.

Also +1 on the fallback ask: if the decision is that mcp_servers stays start-only, the schema/docs should say so and the server should reject rather than accept-and-ignore — either resolution beats the current silent contract violation.