Repository navigation
[Bug]: t3-code MCP credential expires when a turn waits on the user for more than 24 hours #14076
Description
Activity
Triage
Confirmed on
mainatd15210cd3d(same commit cited in the report). The diagnosis matches the code.The bearer is minted once in
prepareMcpSessionwhen the provider session starts (ProviderService.startSession, and session recovery only when no live process is adopted). It is not rotated on later turns.touchActiveMcpThreadruns only fromsendTurnandcompactThread.respondToUserInputandrespondToRequestdo not touch it, and nothing refreshes liveness while a turn is blocked.pruneDeadruns before the refresh inresolve,issue, andtouch. AfterDEFAULT_LIVENESS_WINDOW_MS(24h) with no MCP traffic and no newsendTurn/compactThread, the record is gone. A later answer cannot bring that bearer back, and the provider process keeps presenting it, which is why/mcpreconnect does not help. The 401 isinvalid_mcp_credential; the server warning isrejected MCP request with an unusable credentialwith reasonunknown_or_expired_token.This is the same gap #4659 left open. That change refreshes liveness at the start of each turn, so idle time between turns is covered. A single turn that sits on a user question or an approval for more than 24 hours is not. The same thing happens for any in-progress turn that goes 24 hours without a
t3-codecall. The comment onDEFAULT_LIVENESS_WINDOW_MSsays the window only bounds sessions that died withoutstopSession/stopAll. The implementation never checks that the session is still running.Touching on the answer is not enough for this report: the answer arrived at 44 hours, after the prune. Liveness has to be kept during the wait. The 24h bound should stay, because
/mcpis outside environment auth and this bearer is the only guard. A still-running session (in a turn, or blocked on user input or approval) should count as alive; a session that exits uncleanly should still expire. The existing registry test only covers touches that land inside the window.The restart workaround holds: recovery adopts a still-running process without issuing a new credential, so the next message only gets a fresh bearer after the provider process is gone. Stopping that session and sending a new message does the same thing without restarting the whole server. Sending another message while that process is still up does not.
- addedbugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
on Sep 28, 2026 - added 2 commits that reference this issue
on Sep 28, 2026
Before submitting
Area
apps/server
Steps to reproduce
t3-codeMCP server (seen with Claude).AskUserQuestion) and leave it unanswered for more than 24 hours. Keep the T3 server running the whole time.t3-codetool, for examplelink_pull_request.Observed timeline: the question was asked at 2026-09-26 10:12 UTC, answered at 2026-09-28 06:19 UTC (44 hours later), and the
link_pull_requestcall at 06:39 UTC failed. The server had been running without a restart since 2026-09-25.Expected behavior
A provider session keeps a valid
t3-codeMCP credential for as long as the session is alive, however long a turn runs or waits on the user.Actual behavior
Every
t3-codetool call from that session is rejected:The session cannot recover. The provider process keeps the old bearer in its MCP config, so reconnecting with
/mcppresents the same rejected token.Cause, in
apps/server/src/mcp/McpSessionRegistry.ts:lastAliveAtis older thanDEFAULT_LIVENESS_WINDOW_MS(24 hours) is pruned.lastAliveAtis refreshed only by MCP traffic (resolve) and bytouchActiveMcpThread, whichProviderServicecalls fromsendTurnandcompactThread. Answering a pending user-input request or an approval does not refresh it, and nothing refreshes it while a turn is in progress.resolve,issueandtouchall prune before they refresh, so once 24 hours have passed a later touch cannot bring the record back.Related, but separate:
Impact
Major degradation or frequent failure
Version or commit
0.0.42; the same code is on
main@ d15210cEnvironment
Linux, Claude provider
Workaround
Let the turn finish, then restart the T3 server. The next message resumes the session with a fresh credential.