Skip to content

[Bug]: Claude thread resumes the pre-/clear session, so replies go to the old conversation #24152

Description

@stromseng

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

  1. Work in a Claude thread until it holds a long session (here ~700K tokens, session A).
  2. Send /clear. Claude Code starts a new session B, and the thread keeps working in it.
  3. Run a few turns in B (here a /grill-with-docs skill that asked numbered questions Q1–Q13).
  4. 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 B

Workaround

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
  1. 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.
  2. After /clear, Claude Code reports the new id in system/init (message.session_id). The init handler (around line 9390) only reads permissionMode from it. conversation_reset isn't handled anywhere.
  3. Whenever the process has to be reopened, makeClaudeQueryOptions passes resume: 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

  1. luke2x commented on Oct 23, 2026

    @luke2x
    Contributor

    Note

    Written by gpt 6.1 sol on behalf of Luke

    Opened #24168 for this. It persists the native session ID Claude reports after /clear and 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 /clear or packaged desktop run. Maintainer triage and lifecycle review are still requested.

  2. juliusmarminge commented on Oct 24, 2026

    @juliusmarminge
    Member

    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.

  3. mmenestret commented on Oct 28, 2026

    @mmenestret

    Still reproducing on the nightly, with a trigger that doesn't need a server restart.

    • Version: 0.1.4-nightly.20261027.3770, macOS desktop app, local server. I also checked the source at tag 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.
    • Trigger without a restart: after /clear at 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-/clear session and the agent picked up the old conversation's task.
    • Frequency on this machine: 3 affected threads out of 135 thread logs since Oct 17. In one of them, the old session was resumed on 8 turns between Oct 24 15:53 and Oct 25 09:37, so everything after the /clear was invisible to the model each time.

    Workaround in use until #24168 ships: no /clear in T3, a new thread instead.

    Diagnosed with Claude Code (Claude Opus) following the t3 triage playbook, posted on behalf of Martin.

  4. torbenmadsen commented on Oct 30, 2026

    @torbenmadsen

    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, provider claudeAgent, model claude-haiku-5-5, runtime mode full-access, throwaway thread.

    Steps (deterministic, ~10 minutes):

    1. Create a Claude thread. Tell it a secret word W (e.g. W=QUINCE-8804) and have it read a large file so the context is about 85k tokens.
    2. Read the thread's stored session id: table orchestration_v2_projection_provider_threads, column payload_json, field nativeThreadRef.nativeId (read-only query). Note it as A.
    3. Send /clear as 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.
    4. Read nativeThreadRef.nativeId again: still A, unchanged by the clear.
    5. Send a message that starts a long turn (for example, "read these 20 files one by one"), interrupt it, then send "what is the secret word?".
    6. The thread answers QUINCE-8804 and the context is about 95k tokens: the pre-clear conversation is back.

    Numbers (token counts from usedTokens):

    Step Context Secret word
    before /clear 84,791 known
    after /clear 39,237 unknown
    after interrupt + new message 95,155 known again

    A typed /clear message 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).

  5. mixram commented on Oct 30, 2026

    @mixram

    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:

    1. In a Claude thread, run a turn, then run /clear.
    2. Run another turn in the new session.
    3. Fork the thread from that turn and send a message in the fork.

    Actual result: the fork's turn fails with "Provider turn failed to start: Claude Agent SDK query failed." The trace shows:

    ProviderAdapterForkThreadError: Failed to fork claudeAgent provider thread ...
      [cause]: ClaudeAgentSdkQueryRunnerError: Claude Agent SDK query failed.
        [cause]: TelemetrySafeError: Message 13eb2181-... not found in session 53060466-...
    

    Diagnosis: forkThread takes the session ID from the stale native thread ID. resolveClaudeForkUpToMessageId takes upToMessageId from the turn's nativeTurnRef, which comes from the current session. After /clear, the two IDs belong to different sessions.

    Evidence from this thread:

    • The context transfer records providerThreadRef = 53060466 and providerTurnRef = 13eb2181.
    • The transcript for 53060466 does not contain 13eb2181. The transcript for 574abcbb does, and that transcript starts with /clear.
    • 574abcbb does 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_clear fixture, so it may fix this too. I did not test it.


    Written by Claude Code (Claude Opus 5.5) during t3 triage.

  6. keeperxy commented on Nov 1, 2026

    @keeperxy
    Contributor

    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.4016 on Linux, Claude Code 2.1.316, claudeAgent with 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). A is the session from before /clear, B the one after it:

    Time (UTC) Provider event
    12:25:35 query.open, system/init with session_id A
    13:09:39 / 13:30:25 query.close, then query.open with resume: A. Correct, because no /clear had happened yet
    13:41:37 /clear: conversation_reset, then system/init with session_id B
    13:43–14:27 Four turns in B, each with a system/init reporting B
    14:32:51 The run in B fails with "You've hit your 5-hour limit"
    15:02:51 query.close, exactly 30 minutes later (idle release)
    17:20:04 Usage-limit auto-resume sends "Continue where you left off." → query.open with resume: A, then system/init reports A

    The 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 after A's last pre-/clear reply. 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 in B when the limit hit, was dropped without any notice. Only a later user message brought it back: the agent read B's turns through t3_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 /clear it reopens the old session. The fix in #24168 (persist the session_id from system/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_summary context handoff. It included the first user message from before /clear and the post-resume turns, but none of the four B turns. So the handoff selection doesn't treat /clear as a boundary either.

  7. gavalierm-agent commented on Nov 2, 2026

    @gavalierm-agent

    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, provider claudeAgent). 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: /clear is the first message of a new thread, so the pre-clear session never saves a transcript.

    From ~/.t3/userdata/logs/provider/events.<thread>.log:

    1. query.open with sessionId: A
    2. /clear → conversation_reset, trigger: clear. Claude Code continues in session B and writes B.jsonl; A.jsonl is never written.
    3. Several turns work while the query stays open.
    4. query.close after ~30 min idle.
    5. Next message → query.open with resume: 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.

  8. mixram commented on Nov 3, 2026

    @mixram

    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):

    2026-11-02T16:46:58Z  query.open            sessionId 24557794   (session A, long code review)
    2026-11-03T05:05:46Z  conversation_reset    clear  A -> 848d53e2
    2026-11-03T05:25:57Z  conversation_reset    clear  -> a9554413 (session C)
    05:26-05:28           turns run in C        session_id a9554413
    05:28:19Z             query.interrupt, query.close   (user pressed Stop)
    05:29:04Z             query.open            "resume": "24557794"   <- session A, not C
    05:29-05:33Z          all turns report      session_id 24557794
    

    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.4106 is still affected. Related: #29951 (context meter keeps the pre-clear count; same thread).

  9. jwinpbe commented on Nov 3, 2026

    @jwinpbe

    Note

    Written by Claude Code (Claude Opus 5.5, claude-opus-5-5) via t3 triage, on behalf of the reporter.

    Another trigger: idle compaction ("Before the cache expires"). A server-initiated /compact reopened the Claude query with resume set to the pre-/clear session. 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 touch ClaudeAdapterV2.ts or any session-id or nativeThreadRef handling. 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 still nativeThreadRef.nativeId):

    05:20:42Z  query.open                        session A
    06:58:46Z  conversation_reset clear        → 1f767344
    08:02:14Z  query.open resume=A               ← first /clear undone after a reopen
    08:16:25Z  conversation_reset clear        → c7f068ba
    09:04:54Z  conversation_reset clear        → d86c2f98   (user done with the thread)
    09:34:54Z  query.close                       (30-min idle release)
    09:59:22Z  query.open resume=A, prompt "/compact" (idle compaction)
    10:00:47Z  compact_boundary pre_tokens=283952 post_tokens=14257
    

    Session A's cumulative modelUsage rose 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 /clear at all) is filed separately as #30097, since #24168 doesn't touch IdleCompaction.ts.

  10. gavalierm-agent commented on Nov 3, 2026

    @gavalierm-agent

    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, a SessionStart hook replaces the cursor's transcript ~/.claude/projects/<project>/<cursor>.jsonl with a symlink to the new session's transcript. When T3 later reopens the query with resume: <cursor>, Claude Code follows the link, loads the post-/clear conversation, 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):

    1. I sent /clear, then "the new word is CITRÓN".
    2. T3 reopened the query with resume set to the pre-clear session id.
    3. I asked "what word was in the first message of this conversation you see?". The model answered 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 last query.open resume id in T3's provider log:

    // t3-clear.mjs: SessionStart hook, workaround for t3code#24152
    import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path';
    const input = JSON.parse(fs.readFileSync(0, 'utf8'));
    const thread = process.env.T3CODE_THREAD_ID;
    if (input.source !== 'clear' || !thread) process.exit(0);
    const log = path.join(os.homedir(), '.t3/userdata/logs/provider', `events.${thread.replace(/:/g, '-')}.log`);
    let cursor = null;
    try { const m = [...fs.readFileSync(log, 'utf8').matchAll(/"type":"query\.open","options":\{[^\n]*?"resume":"([0-9a-f-]{36})"/g)]; cursor = m.at(-1)?.[1]; } catch {}
    if (!cursor || cursor === input.session_id) process.exit(0);
    const dir = path.dirname(input.transcript_path), link = path.join(dir, `${cursor}.jsonl`);
    try { const st = fs.lstatSync(link); st.isSymbolicLink() ? fs.unlinkSync(link) : fs.renameSync(link, `${link}.pre-clear-${Date.now()}`); } catch {}
    fs.symlinkSync(`${input.session_id}.jsonl`, link);

    Caveats:

  11. mixram commented on Nov 4, 2026

    @mixram

    Note

    Written by Claude Code (Claude Opus 5.5, claude-opus-5-5) via t3 triage, on behalf of the reporter.

    Confirming the SessionStart symlink 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 resume yet needs the cursor recorded at startup"). On startup and resume, 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 /clear before T3 has ever reopened the query has no query.open … resume line in the log, so the hook can't do anything.

    Verified:

    1. New Claude thread: "The secret word is APPLE."
    2. /clear, then "The new secret word is CITRÓN."
    3. Restarted the T3 desktop app, so T3 reopened the query with resume set to the pre-clear id.
    4. "What secret word do you know?" → CITRÓN.

    Two small changes for macOS desktop installs: the hook command uses an absolute node path, because the desktop app's PATH may not include Homebrew. And the script always exits 0, so a failure can't block session start.

    t3-clear-cursor.mjs
    // SessionStart hook: workaround for pingdotgg/t3code#24152. Remove once #24168 ships.
    import fs from 'node:fs';
    import os from 'node:os';
    import path from 'node:path';
    
    const UUID = /^[0-9a-f-]{36}$/;
    
    function readCursorFromProviderLog(thread) {
      const log = path.join(os.homedir(), '.t3/userdata/logs/provider', `events.${thread.replace(/:/g, '-')}.log`);
      try {
        const text = fs.readFileSync(log, 'utf8');
        const matches = [...text.matchAll(/"type":"query\.open","options":\{[^\n]*?"resume":"([0-9a-f-]{36})"/g)];
        return matches.at(-1)?.[1] ?? null;
      } catch {
        return null;
      }
    }
    
    function main() {
      const thread = process.env.T3CODE_THREAD_ID;
      if (!thread) return;
      const input = JSON.parse(fs.readFileSync(0, 'utf8'));
      const { source, session_id: sessionId, transcript_path: transcriptPath } = input;
      if (!UUID.test(sessionId ?? '') || !transcriptPath) return;
    
      const stateDir = path.join(os.homedir(), '.claude/t3-clear-state');
      const stateFile = path.join(stateDir, `${thread.replace(/[^A-Za-z0-9-]/g, '_')}.cursor`);
    
      // Record the first session id of the thread (covers threads whose first query had no `resume`).
      if (source === 'startup' || source === 'resume') {
        fs.mkdirSync(stateDir, { recursive: true });
        try {
          fs.writeFileSync(stateFile, sessionId, { flag: 'wx' });
        } catch {
          // Already recorded: keep the first id.
        }
        return;
      }
      if (source !== 'clear') return;
    
      let cursor = readCursorFromProviderLog(thread);
      if (!cursor) {
        try {
          cursor = fs.readFileSync(stateFile, 'utf8').trim();
        } catch {
          cursor = null;
        }
      }
      if (!cursor || !UUID.test(cursor) || cursor === sessionId) return;
    
      const link = path.join(path.dirname(transcriptPath), `${cursor}.jsonl`);
      try {
        const st = fs.lstatSync(link);
        if (st.isSymbolicLink()) fs.unlinkSync(link);
        else fs.renameSync(link, `${link}.pre-clear-${Date.now()}`);
      } catch {
        // No transcript for the cursor yet: just create the link.
      }
      fs.symlinkSync(`${sessionId}.jsonl`, link);
    }
    
    try {
      main();
    } catch (error) {
      process.stderr.write(`t3-clear-cursor: ${error.message}\n`);
    }
    process.exit(0);

    ~/.claude/settings.json, added next to any existing SessionStart entries:

    { "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.

  12. Clueed commented on Nov 7, 2026

    @Clueed

    Note

    Written by Claude Code (Claude Opus 5.5, claude-opus-5-5) via t3 triage, on behalf of the reporter.

    On stable 0.1.7 this now fails hard instead of falling back. After /clear and an interrupt, every send fails with No message found with message.uuid of: …. T3 resumes the pre-clear session id with resumeSessionAt set to a message from the post-clear session. The message exists only in the new session's transcript.

  13. Jairoshi commented on Nov 8, 2026

    @Jairoshi

    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.close right after a turn ends, and the next message reopens the query with resume set to the thread's first session.

    From logs/provider/events.<thread>.log*, one thread, five /clears in 17 hours. Every query.open resumed the same first session, 084ad5b7:

    query.open (UTC) Newest session (after /clear) resume sent
    11-08 03:37:52 71e6264c 084ad5b7 ✘
    11-08 05:27:20 9b731af0 084ad5b7 ✘
    11-08 12:49:44 ae26ebc3 084ad5b7 ✘
    11-08 14:07:12 4c6fd3e2 084ad5b7 ✘

    Each reopen follows a query.close sent 0.2–20 s after a result with stop_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 Agent subagent was still writing code. query.close came 0.25 s later, and the subagent's work stopped (last commit 1 minute earlier, nothing after). The session did not call t3_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.)

  14. nicolascvoodoo commented on Nov 9, 2026

    @nicolascvoodoo

    Note

    Written by Claude Code (Claude Opus 5.5, claude-opus-5-5) via t3 triage, on behalf of the reporter.

    Still reproduces on stable 0.1.7 (desktop app, local server, macOS arm64, claudeAgent with claude-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:

    1. In a Claude thread, exchange a few messages (session A).
    2. Send /clear. The SDK emits conversation_reset and continues in session B.
    3. Send a message and wait for the reply (stored in B).
    4. Fork the thread from that reply and send any message in the fork.

    Actual: the fork's first turn fails with "Provider turn failed to start — Claude Agent SDK query failed". T3 retried session.fork 5 times in about 1.5 s, and the fork thread is left failed.

    Timeline (logs/provider/events.<thread>.log, UTC, session IDs shortened):

    14:21:12  conversation_reset (trigger: clear); later messages report session_id 0c898ed0 (B)
    14:28:02  query.interrupt / query.close
    14:28:21  query.open  resume: e2af007f (A, pre-clear)
    14:34:12  session.fork  sessionId: e2af007f, upToMessageId: a2b0f0f0-…   (x5)
    

    Underlying SDK error (server trace, ClaudeAgentSdkQueryRunner.forkSession):

    Message a2b0f0f0-c96c-499e-a4b7-0f3c073f4047 not found in session e2af007f-3d8c-4b10-870c-30f573d90e92
    

    The message exists only in 0c898ed0….jsonl. In the v0.1.7 source, ClaudeAdapterV2.forkThread reads the source session from nativeThreadRef.nativeId, which never moves past the session from before the /clear, because conversation_reset isn't handled anywhere in the adapter. main doesn't handle it either. The thread-fork tests in #24168 look like they cover this case.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions