Repository navigation
Description
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 (orchestration-v2 / ClaudeAdapterV2)
Steps to reproduce
- Create a new Claude (
claudeAgent) thread and send the first message. In my case it had text plus one image attachment, but I don't think the attachment matters. - Press Stop within the first ~2–3 seconds, while the CLI is still starting up (before anything streams back — in my log the SDK had only emitted
command_lifecycle: queued, nosystem/init, no assistant message). - Send any follow-up message (I sent "continue").
Expected behavior
The follow-up starts a turn. The first attempt never reached Claude, so the thread should either open a fresh native session (sessionId) and send the message, or at least offer a way to reset the native session without losing the visible history.
Actual behavior
Every subsequent turn fails instantly with:
Provider error
No conversation found with session ID: 63f9ae9f-a3bf-4601-a74c-2f92cb30d95f
The same error is shown as a toast at the top. The thread is permanently stuck: sending again, restarting the app, or changing the model doesn't help. There is no transcript for that id under ~/.claude/projects/ — Claude Code never got to create the session.
Diagnosis
This is the same outcome as #2336 (closed), but on the V2 code path. #2336 was closed by #13999, which patched apps/server/src/provider/Layers/ClaudeAdapter.ts (V1). That file was deleted on 2026-10-02 by #2829 (new orchestrator), so the fix is gone and ClaudeAdapterV2 doesn't have an equivalent. It is not #15103 (open) — that one is the fresh-session fallback minting a new id and then resuming it; here no fallback runs, the very first id is already dangling.
Timeline from logs/provider/events.<thread>.log, statev2.sqlite (read only) and server.trace.ndjson, times UTC:
| time | what |
|---|---|
| 02:38:05.324 | provider-thread.updated — status: not_loaded, nativeThreadRef: null |
| 02:38:05.383 | provider-thread.updated — nativeThreadRef: { nativeId: "63f9ae9f-…", strength: "strong" }, nativeConversationHeadRef: null. run-attempt.updated also records nativeThreadId: 63f9ae9f-…. This is ~1 s before the SDK query is even opened. |
| 02:38:06.327 | outgoing query.open with sessionId: "63f9ae9f-…" (fresh session, correct) |
| 02:38:06.341 | provider-turn.updated ordinal 1 status: running |
| 02:38:06.786 | incoming command_lifecycle { state: "queued" } — the only thing the CLI ever sent |
| 02:38:08.756 | outgoing query.interrupt (user pressed Stop) |
| 02:38:08.757 | outgoing query.close — 1 ms later, no grace for the CLI to persist anything |
| 02:38:10.765 | provider-turn.updated ordinal 1 status: interrupted |
| 02:38:12.356 | outgoing query.open with resume: "63f9ae9f-…" for the follow-up |
| 02:38:12.803 | incoming result { subtype: "error_during_execution", num_turns: 0, errors: ["No conversation found with session ID: 63f9ae9f-…"] } |
| 02:39:41 | same again for the next message |
No system/init was ever received for that id (grep -c '"type":"system"' on the provider log is 0), and ~/.claude/projects/<cwd>/63f9ae9f-….jsonl does not exist.
Why the follow-up resumes instead of creating, ClaudeAdapterV2.ts openQuery (current main 1e2ecbd):
const hasPersistedProviderTurn =
turnInput.nativeThreadHasTurns ?? turnInput.providerTurnOrdinal > 1;
const shouldResume =
resumeSessionAt !== undefined || openedWithResume || hasPersistedProviderTurn;and ProviderTurnStartService.ts computes nativeThreadHasTurns from attempts that have a provider turn and carry the current native id:
const deliveredAttemptIds = new Set(projection.providerTurns.map((turn) => turn.runAttemptId));
const acceptedAttempts = projection.attempts.filter(
(source) => source.providerThreadId === providerThread.id && deliveredAttemptIds.has(source.id),
);
const nativeInputRunIds = new Set(
acceptedAttempts
.filter((source) => source.nativeThreadId !== undefined &&
source.nativeThreadId === runningProviderThread.nativeThreadRef?.nativeId)
.map((source) => source.runId),
);
// …
nativeThreadHasTurns: nativeInputRunIds.size > 0 || …Run 1 has a provider turn (interrupted) and its attempt carries nativeThreadId: 63f9ae9f-…, so nativeThreadHasTurns is true and the adapter passes resume for a session the CLI never created. The interrupted turn counts as proof that the native conversation exists, but "we sent query.open" and "the CLI wrote a transcript" are different things, and the gap between them is exactly where Stop landed.
Two things seem to combine here:
- The native id is recorded as a
strongref (and the attempt is marked as having that native id) before the SDK acknowledges the session. Claude thread permanently fails with "No conversation found with session ID" after fresh-session fallback resumes a newly minted id #15103 raises the same point for the fallback path. - The V2 interrupt path does
existing.query.interrupt→existing.query.closeback to back, so a Stop during CLI startup never lets the first prompt get persisted. V1 had a 3 s grace for this since fix(server): let Claude abort turns before closing sessions #13999.
Possible directions (not tested):
- Don't count a provider turn toward
nativeThreadHasTurnsunless the CLI confirmed the session (asystem/initor aresultwithsession_idwas received for that native id), or keepnativeThreadRef.strengthas something weaker until then and letopenQueryusesessionIdfor a never-confirmed id. - Port the abort grace from fix(server): let Claude abort turns before closing sessions #13999 to
ClaudeAdapterV2's interrupt handler. - As a safety net: when a
resumeopen returnserror_during_executionwithnum_turns: 0and "No conversation found", retry once withsessionIdfor the same id (or a new one) instead of failing the turn — this would also unbrick the threads that already exist. The thread's history is all in T3, so nothing visible would be lost.
Impact
Blocks work completely — the thread is unusable from the UI.
Version or commit
Desktop AppImage 0.0.46-nightly.20261004.2657. The relevant code is unchanged on main as of 1e2ecbd (2026-10-05).
Environment
Linux x64 (kernel 7.0.0), desktop app (server mode: desktop, loopback), Claude Code CLI 2.1.287, provider claudeAgent, model claude-fable-5-1[1m], runtime mode full-access.
Logs or stack traces
# logs/provider/events.<thread>.log (trimmed; ids other than the session id shortened)
02:38:06.327Z outgoing query.open { sessionId: "63f9ae9f-a3bf-4601-a74c-2f92cb30d95f", model: "claude-fable-5-1[1m]", effort: "medium", permissionMode: "bypassPermissions", cwd: "<project>" }
02:38:06.786Z incoming command_lifecycle { state: "queued", session_id: "63f9ae9f-…" }
02:38:08.756Z outgoing query.interrupt
02:38:08.757Z outgoing query.close
02:38:12.356Z outgoing query.open { resume: "63f9ae9f-a3bf-4601-a74c-2f92cb30d95f", effort: "high", … }
02:38:12.803Z incoming result { subtype: "error_during_execution", is_error: true, num_turns: 0, duration_ms: 0,
errors: ["No conversation found with session ID: 63f9ae9f-a3bf-4601-a74c-2f92cb30d95f"] }
02:39:41.523Z outgoing query.open { resume: "63f9ae9f-…", … }
02:39:41.929Z incoming result { subtype: "error_during_execution", …, errors: ["No conversation found with session ID: 63f9ae9f-…"] }
# statev2.sqlite — orchestration_v2_projection_provider_threads.payload_json (read only)
"nativeThreadRef": {"driver":"claudeAgent","nativeId":"63f9ae9f-a3bf-4601-a74c-2f92cb30d95f","strength":"strong"},
"nativeConversationHeadRef": null, "status": "idle", "firstRunOrdinal": 1, "lastRunOrdinal": 3
# orchestration_events — provider-turn.updated, ordinal 1
02:38:06.341Z status: running
02:38:10.765Z status: interrupted (completedAt 02:38:10.757Z)
# ~/.claude/projects/<cwd>/ → no 63f9ae9f-* transcript exists anywhere under ~/.claudeScreenshots, recordings, or supporting files
Not attaching the screenshot since it shows the prompt content. What it shows: the first user message, then a "Run interrupted · Run interrupted by user" divider, then for each follow-up a "Provider error — No conversation found with session ID: 63f9ae9f-…" row, plus the same text in a red toast at the top of the window.
Workaround
None from inside the app. Starting a new thread works. The stuck one can only be repaired by editing statev2.sqlite (I haven't).
Filed by
Debugged with Claude Code (claude-fable-5-1) running inside T3 Code.
Activity
juliusmarminge commentedon Oct 5, 2026
Note
Grok responding on behalf of Julius.
Triage
Thanks @ThallesP for the thorough report, including the timing and the trace back to #2336. It made the race easy to follow. This reproduces on current
main(1e2ecbd), and it's separate from #15103 and not covered by #15770.What I found
#15103 was the fresh-session fallback, where a newly minted id was opened with
resume; #15770 addressed that by passingnativeThreadHasTurns, which is false for a new id. This thread never takes that path. Claude'sresumeThreadsucceeds, so the fallback doesn't run, and the id that fails is the original one.The sequence on
main:ensureThreadallocates a session id andmakeProviderThreadstores it asstrength: "strong"before the CLI is contacted.ProviderTurnStartServicewrites that id onto the run attempt, thenstartTurnopens the query.openQueryemitsprovider_turn.updated(running) as soon as the SDK query object exists, beforesystem/initor any transcript, so Stop during CLI startup still leaves an interrupted provider turn for that attempt.interruptTurncallsquery.interruptand thenquery.closeimmediately. The 3-second abort grace from fix(server): let Claude abort turns before closing sessions #13999 lived only in the V1ClaudeAdapter(settleInterruptedTurn/CLAUDE_INTERRUPT_GRACE) and went away with that file in feat(orchestrator): introduce new orchestrator #2829. Without it, a Stop that lands while the command is still queued closes the CLI before~/.claude/projects/has a transcript, which matches the 1 ms gap you saw.nativeThreadHasTurnsis true because the interrupted attempt has a provider turn with the same native id, andopenedNativeThreadsis set after a successful SDKopen, which only means the query object was created.shouldResumethen sendsresumeinstead ofsessionId. The CLI returnserror_during_executionwithnum_turns: 0andNo conversation found with session ID: …,providerFailureFromResultfails the turn, and the strong ref stays, so every later send, model change, and restart repeats it.Likely fix area
These line up with the options in your report:
openedNativeThreadsandnativeThreadHasTurnscurrently count the latter).ClaudeAdapterV2. That narrows the race but wouldn't recover threads whose transcript was never written.No conversation foundresult withnum_turns: 0, retry once with a freshsessionIdopen (same id or a new one) instead of failing closed, which would also unstick threads already in this state. The visible history lives in T3, not in the missing Claude transcript.Until then, starting a new thread is the workaround.
A maintainer will decide on the fix direction.
ThallesP commentedon Oct 5, 2026
Proposed fix in #15866:
ClaudeAdapterV2.resumeThreadchecks the CLI's session storage before a resume and fails when there is no conversation, so the turn start falls back to a fresh native session. The PR also documents a CLI window (aftersystem/init, before the first persisted prompt) where the id is dead for both--resumeand--session-id, which is why the check is reactive instead of a persisted flag.Dawidekk523 commentedon Oct 18, 2026
Same root cause as described on #20334, different trigger — posting a real-world occurrence in case it helps #15866.
Setup
0.1.0(com.t3tools.t3code, latest release v0.1.0), Claude Code CLI2.1.302, macOS, Claude Opus 5.5.claudeAgent->homePath: ~/.claude-t3claudeAgent2("Claude 2 (max2)") ->homePath: ~/.claude-t3-max2Trigger: the source account hit its usage limit, so T3 handed the thread to the other instance.
The thread (
957cfee0-..., cwd~/Desktop/gry/magazyn) was running onclaudeAgent. On the last turn of that account:{"type":"result","is_error":true,"api_error_status":429,"api_error_origin":"first_party","api_error":"usage_limit_reached","result":"You've hit your session limit \u00b7 resets 1:50pm (Europe/Warsaw)","session_id":"fa50b488-8431-41eb-ae5e-726e86f70753"}T3 then ran a context handoff to
claudeAgent2(delta bundle: "Covered app runs: 116-263", "Selected 10 intact items; omitted 585 items") and opened the query on the target with the source account's native session id:{"type":"query.open","options":{"model":"claude-opus-5-5","resume":"0de286a3-d8f8-48ec-a463-8d12e2a95f12","cwd":"~/Desktop/gry/magazyn","permissionMode":"bypassPermissions"}}Result: every turn on the target fails identically and no fresh-session fallback takes over.
{"type":"result","subtype":"error_during_execution","is_error":true,"num_turns":0,"session_id":"0de286a3-...","errors":["No conversation found with session ID: 0de286a3-d8f8-48ec-a463-8d12e2a95f12"],"duration_ms":0}Three consecutive failures: two follow-ups ("go on") and a delegated-subagent completion notification. The thread is wedged — the UI shows "Provider error" and the handoff stays "Context handoff pending"; a still-running delegated subagent keeps producing notifications that all hit the same wall.
Why the id cannot be found: the transcript exists only in the source home,
~/.claude-t3/projects/-Users-dawid-Desktop-gry-magazyn/0de286a3-....jsonl(its CLI continuation isfa50b488-....jsonl). In the target home,~/.claude-t3-max2/projects/-Users-dawid-Desktop-gry-magazyn/contains no transcripts at all (onlymemory/).The stale id belongs to the thread, not to the instance, so it survives the switch — same as the maintainer's explanation on #20334. One extra detail that may matter for the handoff path: this thread has been hopping between the two accounts, an earlier handoff at run 116 went
from-provider-instance:claudeAgent2 to-provider-instance:claudeAgent:e28e04c9-..., so the persisted native id is whichever account ran the thread last.I have not rebound the thread yet; per #20334 switching it to an ACP provider is the recovery that worked there.
MattRiddell commentedon Oct 18, 2026
We hit this repeatedly on a headless
t3 serve(0.0.4x nightlies of 2026-09-19 and 2026-09-21; the resume path is unchanged in 0.1.1-nightly.20261017.3238) and have a deterministic reproduction plus a concrete ask.Root cause as we read it.
updateResumeCursorstores whatever session id the Claude CLI reports for the session that is starting. If that session dies before it flushes its transcript (we kill it two seconds after start; a user typing into a thread while its model is being re-pinned does the same), no<id>.jsonlis ever written, but T3 already stored the id. From then on every turn runs--resume <id>and fails withNo conversation found with session ID <id>, and the cursor is never re-derived. The thread's status still readsready, so nothing looks wrong until someone types.Reproduction (scratch thread, 2026-09-21):
claudeAgentthread, one completed turn. Cursorresume=c362d324…, transcript present.resume=70c59a34…, for which no transcript exists on disk.turn/setPermissionMode failed,No conversation found with session ID 70c59a34…,Claude runtime stream failed. Permanently.What we ask for, most useful first:
No conversation found: start a fresh session for the thread instead of failing every turn forever.thread.session.set { resumeCursor: null }or a--fresh-sessionturn option). Todaythread/rollbackclears it only whennumTurnscovers every turn, which discards the conversation.Workaround we use: re-pin the thread to the home it came from, stop it, and wake it. Sometimes that recovers the original session; sometimes it mints a new one and the history is gone. Which one happens is not under the operator's control, so a real reset would help a lot. Happy to test #15866 on our server.
juliusmarminge commentedon Oct 22, 2026
Verification blocked on this Linux triage VM at main b5a226e49c: the required T3 Browser tools are absent (
preview_statusandpreview_openboth return tool-not-found). No stop-before-init attempt ran, so this is not a failure to reproduce. The existing report provides detailed steps and logs; no additional reporter information is needed yet. This needs a triage environment with the collaborative Browser panel to verify the first-message → Stop before init → follow-up sequence. Leaving open.gnestor commentedon Oct 22, 2026
Another occurrence, with a different trigger: T3 moved the thread to a fresh native session, but the turn that opened it never sent the CLI a prompt. Fresh ids can therefore end up never persisted even when nobody presses Stop. #15866's reactive check would cover this.
Setup: T3 Code
0.1.1(macOS), Claude Code CLI2.1.308,claude-opus-5-5. A long-running Claude thread, 268 runs; its native transcript was about 270 MB.Sequence (UTC, 2026-10-22), from
logs/provider/events.<thread>.logandstatev2.sqlite(read only):a0680532-….kind: background_task).context-handoff.updatedstrategy: full_thread_summary, pluscontext-transfer.updatedresolution: portable_context.provider-thread.updatedsetsnativeThreadRefto a new idb756919e-…(strength: strong).run-attempt.updatedstamps the same id.a0680532-…) is closed.query.openwithsessionId: b756919e-…. That is correct for a new id. The CLI runs its SessionStart hooks (hook_started/hook_responsecarrysession_id: b756919e-…).prompt.offeris ever sent on this query.query.close(idle).query.openwithresume: b756919e-…, thenresult/error_during_execution,num_turns: 0, "No conversation found with session ID: b756919e-…".So the new id got a provider turn (run 268,
completed), and its attempt carries the native id. That is enough fornativeThreadHasTurnsto be true, but the CLI never persisted a transcript, because it writes the JSONL only when the first prompt arrives.~/.claude/projects/<cwd>/b756919e-….jsonldid not exist. The originala0680532-….jsonlwas intact the whole time.Also broken: fork. Forking the thread from an earlier message sends
session.forkwithsessionId: b756919e-…andupToMessageId: <a message in a0680532>. It fails with "Claude Agent SDK query failed.", so forking doesn't work as an escape hatch either.Workaround that unbricked it: copy the original transcript to the missing id,
cp a0680532-….jsonl b756919e-….jsonl(plus thea0680532-…/subagents folder), in the same project directory. The next turn resumedb756919e-…normally.Two notes for #15866:
system/initor hook events for an id don't prove a transcript exists. Here the CLI emitted session-scoped events forb756919e-…, but no prompt ever reached it.nativeThreadHasTurnsfor a freshly minted id.DrugsAreMyLife commentedon Oct 26, 2026
Another occurrence, with a third trigger: switching a thread back to a provider instance whose only earlier run failed before the CLI started. #15866's reactive check would stop the error, but this case shows a second effect that the check alone doesn't fix: the context handoff.
Setup: T3 Code
0.1.3-nightly.20261025.3655(Linux, WSL2), Claude Code CLI2.1.310,claude-opus-5-5. ThreeclaudeAgentinstances in one workspace: instance P (custombinaryPathlauncher) and native instances A and B, each with its own home.Sequence (UTC, 2026-10-25):
system/initand no transcript. T3 still stored native id1a54ba68-1d63-4290-ac61-e4cb4327abd5for P.Covered app runs: 2-103. P is treated as having participated in run 1. T3 resumes1a54ba68-…, and the turn fails withNo conversation found with session ID: 1a54ba68-1d63-4290-ac61-e4cb4327abd5. There is no1a54ba68-*.jsonlanywhere under P's home.1-104and works.Second effect, the handoff.
docs/internals/context-handoffs.mdsends "the delta since that provider last participated". A run that failed before the CLI ever started still counts as participation. So even if #15866's check makes P fall back to a fresh native session, that fresh session would get only the delta2-103. It would miss run 1, which here holds the thread's first user message.Suggested addition: when the turn start falls back to a fresh native session (or the stored id has no persisted conversation), treat the instance as never having participated, and send the full handoff.
Workaround: move the thread to an instance that has never run in it. Check that the handoff item covers run 1.
tristanmanchester commentedon Oct 31, 2026
Another occurrence, with a different trigger: moving to a new OS install without carrying over every Claude transcript.
Setup: T3 Code
0.1.3-nightly.20261027.3754, Linux, two native Claude instances with separate config directories, used from the mobile app.Sequence: A long-running thread's provider thread on instance A kept a
strongnativeThreadRef, with the last successful turn on 2026-10-23. The transcript stayed in the old home directory and is absent from A's config directory. The thread then ran on instance B. Each switch back to A (2026-10-28 and 2026-10-31) opened a query withresume: <id>and goterror_during_execution/No conversation found with session IDin about 15ms. Switching back to B works.Cause:
resumeThreadcallshasEmptyClaudeTranscript, which returns false when no transcript file exists, so theprovider_resume_fallbackpath never runs. The failed turn also leavesnativeThreadRefin place. The check is unchanged onmainat961026c6.Scope: 8 of 11 provider threads on this instance reference sessions missing from its config directory. All 8 transcripts exist in the old home directory.
#15866 describes the fix this needs: treating a definitely missing transcript as eligible for fresh-session recovery. It would also recover these threads.
Produced by Claude Opus 5.5 in Claude Code via T3 triage.
znxr commentedon Nov 2, 2026
Another occurrence, with a different trigger: the first turn after the V1 → V2 migration of a long legacy Claude thread. Nobody pressed Stop.
Setup: T3 Code
0.1.6(Linux),claudeAgent,claude-opus-5-5. Long-running thread (115 turns, three auto-compactions). Its native transcript is ~166 MB.Sequence (UTC), from
logs/provider/events.<thread>.log,state.sqliteandstatev2.sqlite(read only):provider_session_runtime.resume_cursor_jsoninstate.sqliteholdsresume: f5b96a67-…. That transcript exists and still resumes fine withclaude --resume.orchestration_v2_legacy_imports: 682 messages, nolast_error). The native session is not reused. Amanual_contexthandoff fromlegacyis created, and the provider thread gets a newnativeThreadRef: { nativeId: "4c78df73-…", strength: "strong" }.query.openwithsessionId: "4c78df73-…"(fresh) +prompt.offerwith the handoff (~14 KB). No incoming event at all is logged for this run: nocommand_lifecycle, nosystem/init, noresult.~/.claude/projects/<cwd>/4c78df73-….jsonlis never created.query.openwithresume: "4c78df73-…". Again nothing incoming is logged.query.openwithresume: "4c78df73-…"→result { subtype: "error_during_execution", num_turns: 0, errors: ["No conversation found with session ID: 4c78df73-…"] }All six run attempts are
failed, and the provider thread keeps the same strong native ref, so the thread can't recover. #15866's reactive check would cover the error.Starting a fresh session after migration is expected (#14871). The problem is the same as in this issue: the first fresh run never persisted, and every later turn resumes the dangling id. One difference from the original report: I don't know why the first run produced no CLI output at all. Even
command_lifecycle: queuednever arrived, and nobody pressed Stop.Redacted provider log excerpt (prompt contents and paths removed)
Workaround:
claude --resume f5b96a67-…outside T3 works.