rmcp MCP OAuth sessions do not recover on 401 invalid_token; stale session fails while new session succeeds
Summary
For OAuth-enabled remote MCP servers using the rmcp streamable HTTP transport, an existing Codex session can get stuck sending a stale access token. When the MCP server returns 401 with WWW-Authenticate: Bearer error="invalid_token", Codex surfaces Auth required and does not refresh/retry. Starting a brand new Codex session succeeds immediately with the same MCP server/account.
This looks like a real recovery gap distinct from the older expires_at refresh bug.
Expected behavior
If an MCP server returns 401 invalid_token for an OAuth-backed MCP connection, Codex should attempt OAuth recovery:
- refresh the OAuth token if possible
- retry the MCP request once with the fresh token
At minimum, existing sessions should recover the same way a freshly started session does.
Observed behavior
- In one running Codex session, MCP tool calls start failing with:
Transport ... AuthClient ... error: Auth required
- The MCP resource server logs show it rejected an expired bearer token and returned
401/invalid_token. - A new Codex session using the same MCP server and same user succeeds immediately.
That strongly suggests the old session is holding stale auth state and not recovering from a 401.
What I found in the current source
Current main has proactive refresh based on stored expires_at in OAuthPersistor::refresh_if_needed, and MCP operations call that before each request.
But the rmcp client test suite also explicitly asserts that 401 does not trigger recovery:
codex-rs/rmcp-client/tests/streamable_http_recovery.rs- test:
streamable_http_401_does_not_trigger_recovery
So the current behavior appears to be:
- proactive refresh only, if local expiry metadata says it is needed
- no refresh/retry path when the server actually returns
401 invalid_token
That matches the real-world symptom above.
Steps to reproduce
I reproduced this against a custom OAuth-enabled remote MCP server, but the pattern should apply to any MCP server that returns OAuth-style 401 invalid_token when the access token expires server-side.
- Authenticate an OAuth-backed remote MCP server in Codex.
- Use the MCP server successfully in a running Codex session.
- Let the access token expire or otherwise become invalid server-side.
- In the same running session, invoke an MCP tool.
- Observe
Auth requiredfrom the rmcp transport. - Start a fresh Codex session.
- Observe the same MCP server now works immediately.
Why this seems distinct from existing issues
Related, but not exact duplicates:
- #6572 fixed/identified the
expires_at-based proactive refresh problem. - #14144 is about reauth succeeding but the active session still using stale refresh-token state.
- #7318 asks for config/header reload on 401/403, but this report is specifically about OAuth-backed MCP recovery.
This issue is narrower:
- existing session receives
401 invalid_token - Codex does not recover
- new session succeeds
- current rmcp test coverage appears to codify the non-recovery behavior
Proposed fix direction
For OAuth-backed rmcp MCP connections, consider treating 401 / WWW-Authenticate: Bearer error="invalid_token" as recoverable once:
- attempt token refresh (or re-load refreshed credentials)
- reinitialize transport if needed
- retry the request once
Environment
Observed on macOS in March 2026 against a custom remote MCP server.
If helpful, I can add more detail from the server-side logs, but the key signal was:
- stale session ->
Auth required - new session -> same
get_meMCP call succeeds immediately
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗