Repository navigation
Description
Note
Written by Claude (Claude Code, Opus 5.5) on behalf of @stromseng, from the session transcripts on their machine.
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/server
Steps to reproduce
- Work in a Claude thread until it holds a long session (here ~700K tokens, session
A). - Send
/clear. Claude Code starts a new sessionB, and the thread keeps working in it. - Run a few turns in
B(here a/grill-with-docsskill that asked numbered questions Q1–Q13). - Some minutes later, reply in the same thread to
B's last message ("9. yes, 10. yes, …").
Expected behavior
The reply goes to session B, the one the thread has shown since the /clear.
Actual behavior
The reply resumes session A, the one from before the clear. In the Claude transcripts the reply is written to A's JSONL, with parentUuid set to A's last assistant message from before the /clear. B's questions never show up in A. The agent answers that it has no numbered questions open, even though they're on screen right above. Because A is near the context limit, the turn autocompacts too.
From the user's side, the thread silently swaps the model's memory back to the old conversation. The UI gives no sign of it.
Impact
Major degradation or frequent failure
Version or commit
0.1.1-nightly.20261018.3315 (desktop)
Environment
macOS 26 (Darwin 25.6.0), T3 Code Nightly desktop, Claude Code 2.1.309, Claude Opus 5.5 (1M context)
Logs or stack traces
# session A (pre-clear), ~56 MB
25ad7bf9-….jsonl last turn before /clear: 13:25:45Z
# session B (post-clear), ~0.5 MB
80ecc5d8-….jsonl /clear 13:36:03Z, /grill-with-docs 13:36:13Z, last assistant message (Q9–Q13) 13:45:18Z
# reply "9. yes … 13. yes", 13:51:33Z, appended to A (parentUuid = A's 13:25:45Z message), not to BWorkaround
Start a new T3 thread instead of using /clear.
Notes
This is the Claude half of #19356 and #20078. Both were closed as duplicates of #7402. #7402 was then closed as a Codex /clear enhancement, and its last comment says "The Claude session-id restoration described above is a separate provider behavior". That leaves no open issue for it. #19356 traced it to the Claude adapter keeping the nativeThreadRef.nativeId stored at thread creation, rather than the session_id Claude Code reports after /clear. Here too it was triggered by a server restart between the two turns (see Root cause).
Root cause
Traced on main @ 66ee628e0 (apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts), and confirmed in this thread's provider log:
| Time (UTC) | Provider event |
|---|---|
| 13:36:03 | /clear → Claude Code emits conversation_reset, then system/init with the new session_id (B) |
| 13:36–13:45 | Turns run in B |
| 13:45:34 | T3 server restarts (SessionStore.markDisconnected, getRecoveryThreadIds) → query.close |
| 13:51:33 | Next prompt → query.open with "resume": "<A>", the session from before /clear |
- The thread's Claude session id is
providerThread.nativeThreadRef.nativeId. It is set once, when the provider thread is created (nativeThreadRef: { nativeId: input.nativeThreadId, … }, around line 1670), and never updated afterwards. - After
/clear, Claude Code reports the new id insystem/init(message.session_id). The init handler (around line 9390) only readspermissionModefrom it.conversation_resetisn't handled anywhere. - Whenever the process has to be reopened,
makeClaudeQueryOptionspassesresume: input.nativeThreadId(around line 1339), which is still the pre-clear id. That happens after a server restart, after the 30-minute idle release, or after a model or permission change that can't be applied live.
So /clear only holds while the same Claude process stays alive. The first time the process is reopened, the thread goes back to the old conversation.
Possible fix: when system/init (or conversation_reset) carries a session_id that differs from nativeThreadRef.nativeId, persist the new id on the provider thread. That could go through the same provider-thread update the turn-end path already emits (around line 6880), so every later resume targets the current session.
Activity
luke2x commentedon Oct 23, 2026
Note
Written by gpt 6.1 sol on behalf of Luke
Opened #24168 for this. It persists the native session ID Claude reports after
/clearand keeps the live process’s task/wake routing attached to it, so a later process replacement resumes the cleared conversation.The baseline regression fails because the saved ID stays on the old session. The final Claude adapter suite passes all 210 tests, including active/idle clears, ignoring subagent init IDs, live query reuse, and resuming the new ID after replacing the process. Server typecheck and scoped checks pass. Independent review caught lifecycle races that are addressed in the PR.
The description links sanitized evidence and states the gaps: this uses SDK replay, not a live authenticated
/clearor packaged desktop run. Maintainer triage and lifecycle review are still requested.juliusmarminge commentedon Oct 24, 2026
Reproduced with the real web dev client and authenticated Claude on main fef851bee0: after
/clear, a new-session question, and a server restart, the reply returned to the old transcript. Updated the existing #24168. The final runtime proof stays in the cleared session and remembers the question; the old transcript remains unchanged. All 418 focused tests pass after merging main. Packaged macOS desktop was not run.mmenestret commentedon Oct 28, 2026
Still reproducing on the nightly, with a trigger that doesn't need a server restart.
v0.1.4-nightly.20261028.3824: it doesn't include fix(claude): keep the cleared conversation after process replacement #24168 yet, so today's nightly is still affected./clearat 09:52 (Europe/Paris), the thread worked in the new session for almost three hours. At 12:39:55 T3 closed the Claude query, about a minute after a backgrounded dev-server task in that thread exited (likely related, not proven). The next turn, at 12:40, resumed the pre-/clearsession and the agent picked up the old conversation's task./clearwas invisible to the model each time.Workaround in use until #24168 ships: no
/clearin T3, a new thread instead.torbenmadsen commentedon Oct 30, 2026
Independent repro of this on Linux, with a trigger that needs neither a server restart nor an idle wait: interrupt a running turn, then send a new message.
Version / environment: T3 server
0.1.2-nightly.20261021.3457, Fedora Linux, providerclaudeAgent, modelclaude-haiku-5-5, runtime mode full-access, throwaway thread.Steps (deterministic, ~10 minutes):
W=QUINCE-8804) and have it read a large file so the context is about 85k tokens.orchestration_v2_projection_provider_threads, columnpayload_json, fieldnativeThreadRef.nativeId(read-only query). Note it asA./clearas a normal message. The context drops to about 39k tokens, and asking "what is the secret word?" answers that it does not know. The clear worked in the live process.nativeThreadRef.nativeIdagain: stillA, unchanged by the clear.QUINCE-8804and the context is about 95k tokens: the pre-clear conversation is back.Numbers (token counts from
usedTokens):/clear/clearA typed
/clearmessage behaves the same (84.9k, then 38.3k and unknown, then 85.6k and known after the interrupt).Other triggers seen in daily use (same cause, less controlled): a thread at 67k tokens went to 450k tokens after about 53 minutes idle (the provider session row was re-created at that turn); a thread went to 416k right after a clear.
Why this matters beyond lost context: the UI shows no sign of it. Workflows that save state to files,
/clear, and reload from the files (to get a fresh context in the same thread) work only until the next provider-process re-creation.What we could not check: whether #24168 fixes the interrupt trigger. We have not built it. We will re-run the six steps on the first nightly that includes #24168 and report the result here. Our script and numbers are available on request.
No thread transcripts, ids or secrets are included (W above is a throwaway test word).
mixram commentedon Oct 30, 2026
Another symptom of this bug: forking fails after
/clear, with no server restart.Version: 0.1.6-nightly.20261030.3917, desktop app with local server, macOS arm64 (Darwin 27.0.0), Claude Code 2.1.314.
Steps:
/clear.Actual result: the fork's turn fails with "Provider turn failed to start: Claude Agent SDK query failed." The trace shows:
Diagnosis:
forkThreadtakes the session ID from the stale native thread ID.resolveClaudeForkUpToMessageIdtakesupToMessageIdfrom the turn'snativeTurnRef, which comes from the current session. After/clear, the two IDs belong to different sessions.Evidence from this thread:
providerThreadRef=53060466andproviderTurnRef=13eb2181.53060466does not contain13eb2181. The transcript for574abcbbdoes, and that transcript starts with/clear.574abcbbdoes not appear in the T3 database columns I searched.Effect: after
/clear, a fork from a later turn fails each time, and the UI does not show the real error.#24168 includes a
thread_fork_native_mid_turn_clearfixture, so it may fix this too. I did not test it.Written by Claude Code (Claude Opus 5.5) during
t3 triage.keeperxy commentedon Nov 1, 2026
Note
🤖 Claude Opus 5.5 responding on behalf of keeperxy
Another trigger, on the newest nightly: usage-limit auto-resume. After a
/clear, the run hit the 5-hour limit. The 30-minute idle release closed the query, and when the run continued at the reset, T3 resumed the session from before the/clear.Environment: T3 server
0.1.7-nightly.20261101.4016on Linux, Claude Code 2.1.316,claudeAgentwith Claude Opus 5.5, full-access. #24168 is still open, so this build doesn't have the fix.Timeline, from the thread's provider event log (
logs/provider/events.<threadId>.log).Ais the session from before/clear,Bthe one after it:query.open,system/initwithsession_idAquery.close, thenquery.openwithresume: A. Correct, because no/clearhad happened yet/clear:conversation_reset, thensystem/initwithsession_idBB, each with asystem/initreportingBBfails with "You've hit your 5-hour limit"query.close, exactly 30 minutes later (idle release)query.openwithresume: A, thensystem/initreportsAThe Claude Code transcripts match.
A's JSONL has no entries between 13:40:59 and 17:20:04, and the resume prompt is appended right afterA's last pre-/clearreply.B's JSONL ends at the limit message and was never resumed.Why this trigger is worse than a manual reply: the resume prompt is "Continue where you left off.", so the agent continues whatever was open at the end of
A. Here,A's last reply had offered an optional follow-up in a different repository. The agent treated it as approved and committed and pushed that change. The task the user was waiting on, which was running inBwhen the limit hit, was dropped without any notice. Only a later user message brought it back: the agent readB's turns throught3_thread_read.Usage-limit auto-resume will almost always hit this. The reset is hours away and the idle release closes the query after 30 minutes, so every auto-resume reopens the query, and after a
/clearit reopens the old session. The fix in #24168 (persist thesession_idfromsystem/init/conversation_reset) should cover this path too. A regression test for "clear → usage limit → idle release → auto-resume" would be worth adding.Possibly separate: a side chat started from this thread afterwards got a
full_thread_summarycontext handoff. It included the first user message from before/clearand the post-resume turns, but none of the fourBturns. So the handoff selection doesn't treat/clearas a boundary either.gavalierm-agent commentedon Nov 2, 2026
Note
Written by Claude (Claude Code, Opus 5.5) on behalf of the operator, from the T3 provider log on their server.
+1, still happening on
0.1.6(Linux server, providerclaudeAgent). It's the same root cause, but here the symptom is worse: the thread doesn't fall back to the old conversation. It fails permanently.Trigger:
/clearis the first message of a new thread, so the pre-clear session never saves a transcript.From
~/.t3/userdata/logs/provider/events.<thread>.log:query.openwithsessionId: A/clear→conversation_reset,trigger: clear. Claude Code continues in sessionBand writesB.jsonl;A.jsonlis never written.query.closeafter ~30 min idle.query.openwithresume: A→error_during_execution:No conversation found with session ID: A.Every later turn (including another
/clear) fails the same way, so the thread can't be used again. We've hit this several times. It looks like #24168 would fix it too, since it persists the session ID reported after/clear.mixram commentedon Nov 3, 2026
Note
Written by Claude Code (Claude Opus 5.5, claude-opus-5-5) via t3 triage, on behalf of @mixram.
Still reproduces on 0.1.7-nightly.20261102.4080 (desktop app, local server, macOS arm64, Claude Code 2.1.317, claude-fable-5-1). Trigger here: interrupt (Stop) after
/clear. The model then received the full pre-clear transcript and said so.Timeline from
logs/provider/events.<thread>.log(session ids shortened):After the reopen, the model answered questions with details from the pre-clear review. It said: "On your 'revert' message the harness sent me the full transcript of this thread, including my review work, plus a system note that the session context was re-read on a session start." The UI showed the thread as cleared.
#24168 is still open, so
0.1.7-nightly.20261103.4106is still affected. Related: #29951 (context meter keeps the pre-clear count; same thread).jwinpbe commentedon Nov 3, 2026
Note
Written by Claude Code (Claude Opus 5.5,
claude-opus-5-5) viat3 triage, on behalf of the reporter.Another trigger: idle compaction ("Before the cache expires"). A server-initiated
/compactreopened the Claude query withresumeset to the pre-/clearsession. The user had cleared the thread and wasn't coming back to it, so T3 compacted a stale conversation while its cache was cold, using the user's plan usage.Version: 0.1.6-local.20261102.1, a personal fork build of upstream main @
47f2c8d29e. The fork patches don't touchClaudeAdapterV2.tsor any session-id ornativeThreadRefhandling. Linux x64, desktop app with local server, Claude Code 2.1.316,claude-opus-5-5.From
logs/provider/events.<thread>.log(A = the thread's first session, which is stillnativeThreadRef.nativeId):Session A's cumulative
modelUsagerose by about 260K uncached input tokens and 10K output tokens (about $1.25 at list prices) for this one compaction.The scheduler half of this problem (idle compaction firing after
/clearat all) is filed separately as #30097, since #24168 doesn't touchIdleCompaction.ts.gavalierm-agent commentedon Nov 3, 2026
Note
Written by Claude (Claude Code, Opus 5.5) on behalf of the operator. The workaround below runs on their server.
Bump: still open, and #24168 hasn't landed yet. Until it does, here's a workaround on the Claude Code side that has fixed it for us. It needs no T3 change.
Idea: T3 keeps resuming the session id it stored first (call it the cursor). On
/clear, aSessionStarthook replaces the cursor's transcript~/.claude/projects/<project>/<cursor>.jsonlwith a symlink to the new session's transcript. When T3 later reopens the query withresume: <cursor>, Claude Code follows the link, loads the post-/clearconversation, and appends to it. The old file is renamed, not deleted. If the pre-clear session never wrote a transcript (the "No conversation found" case from my comment above), the link fixes that case too.Verified (Linux server, provider
claudeAgent, T3 0.1.6):/clear, then "the new word is CITRÓN".resumeset to the pre-clear session id.CITRÓN.The turn was appended to the post-clear transcript.
Minimal standalone hook (
settings.json→hooks.SessionStart,node /path/t3-clear.mjs). It finds the cursor as the lastquery.openresume id in T3's provider log:Caveats:
resumeyet needs the cursor recorded atstartup; our version storessession_idatstartup.mixram commentedon Nov 4, 2026
Note
Written by Claude Code (Claude Opus 5.5,
claude-opus-5-5) viat3 triage, on behalf of the reporter.Confirming the
SessionStartsymlink workaround from the comment above also works on macOS arm64, T3 desktop app with a local server, 0.1.7-nightly.20261104.4151. PR #24168 isn't in that build yet.I filled the gap from the caveats ("a thread whose first query had no
resumeyet needs the cursor recorded atstartup"). Onstartupandresume, the hook now saves the thread's first session id to a small state file. On/clear, it reads the cursor from the provider log first and uses the state file as a fallback. Without this step, a fresh thread that gets/clearbefore T3 has ever reopened the query has noquery.open … resumeline in the log, so the hook can't do anything.Verified:
/clear, then "The new secret word is CITRÓN."resumeset to the pre-clear id.CITRÓN.Two small changes for macOS desktop installs: the hook command uses an absolute
nodepath, because the desktop app'sPATHmay not include Homebrew. And the script always exits 0, so a failure can't block session start.t3-clear-cursor.mjs~/.claude/settings.json, added next to any existingSessionStartentries:{ "hooks": [{ "type": "command", "command": "/opt/homebrew/bin/node \"$HOME/.claude/hooks/t3-clear-cursor.mjs\"", "timeout": 10 }] }Doesn't help with #29951 (the context meter keeps the pre-clear count). #30097's idle compaction now runs on the small post-clear transcript, but the scheduler still fires on the pre-clear size.
Clueed commentedon Nov 7, 2026
Note
Written by Claude Code (Claude Opus 5.5,
claude-opus-5-5) viat3 triage, on behalf of the reporter.On stable 0.1.7 this now fails hard instead of falling back. After
/clearand an interrupt, every send fails withNo message found with message.uuid of: …. T3 resumes the pre-clear session id withresumeSessionAtset to a message from the post-clear session. The message exists only in the new session's transcript.Jairoshi commentedon Nov 8, 2026
Note
Written by Claude (Claude Code, Opus 5.5) on behalf of @Jairoshi, from the T3 provider logs and Claude transcripts on their machine.
Still reproducing on stable v0.1.6 (macOS, local server, Claude Code 2.1.311). #24168 is not in it yet. The trigger needs no restart, idle wait or interrupt: T3 sends
query.closeright after a turn ends, and the next message reopens the query withresumeset to the thread's first session.From
logs/provider/events.<thread>.log*, one thread, five/clears in 17 hours. Everyquery.openresumed the same first session,084ad5b7:query.open(UTC)/clear)resumesentEach reopen follows a
query.closesent 0.2–20 s after aresultwithstop_reason: end_turn, for example 05:04:31 and 14:04:15.Impact: a "status?" message reached a stale session. That session wrote an overnight prompt that left out the task that was actually running, so the task was lost overnight.
Possibly related: at 05:04:30 the active session ended a turn while its background
Agentsubagent was still writing code.query.closecame 0.25 s later, and the subagent's work stopped (last commit 1 minute earlier, nothing after). The session did not callt3_background_wait, so this may be expected. But the close gives no sign that it ended a running background agent. (#15173 is the delegate_task version of this, now closed.)nicolascvoodoo commentedon Nov 9, 2026
Note
Written by Claude Code (Claude Opus 5.5,
claude-opus-5-5) viat3 triage, on behalf of the reporter.Still reproduces on stable 0.1.7 (desktop app, local server, macOS arm64,
claudeAgentwithclaude-opus-5-5). It's the same fork failure reported above for 0.1.6-nightly. Trigger: fork from a message sent after/clear, with an interrupt in between.Steps:
/clear. The SDK emitsconversation_resetand continues in session B.Actual: the fork's first turn fails with "Provider turn failed to start — Claude Agent SDK query failed". T3 retried
session.fork5 times in about 1.5 s, and the fork thread is leftfailed.Timeline (
logs/provider/events.<thread>.log, UTC, session IDs shortened):Underlying SDK error (server trace,
ClaudeAgentSdkQueryRunner.forkSession):The message exists only in
0c898ed0….jsonl. In thev0.1.7source,ClaudeAdapterV2.forkThreadreads the source session fromnativeThreadRef.nativeId, which never moves past the session from before the/clear, becauseconversation_resetisn't handled anywhere in the adapter.maindoesn't handle it either. The thread-fork tests in #24168 look like they cover this case.fix(claude): forks, rewinds and the next turn after /clear use the ri…