Repository navigation
Claude thread loses context and cannot fork after its provider association moves to an imported thread #17236
Description
What happened
An existing Claude conversation continued displaying its history in T3, but the agent could no longer see the earlier messages. Forking from a response before the context loss also failed.
Investigation found a separate imported thread associated with the original Claude session. Attempting to continue that imported thread failed with a database uniqueness error.
The conversation text and native Claude session file were still present. Recovery required exporting the saved messages and having a fresh thread read them.
Diagnosis
The persisted state suggests a collision between an existing thread and an imported representation of the same native Claude session.
Using synthetic identifiers:
- Original application thread
Ahas runs 2–19 referencing provider threadP. - The provider-thread record for
Phas boththread_idandpayload_json.appThreadIdpointing toimport:claudeAgent:SESSION_X, rather thanA. - The imported thread’s active provider thread is
P. - Run 20 in
Astarts a different native Claude conversation. Runs 20–22 have no recordedcontextHandoffId. - A fork from run 19 fails because T3 cannot resolve the source provider thread.
- Continuing the imported thread fails on the unique constraint for provider-thread ID plus turn ordinal.
The fork error matches the guard in Orchestrator.ts that rejects a pending fork when its source run or source provider thread cannot be resolved.
Suspected cause: importing the native session reassigned a provider-thread record already referenced by the original application thread. The resulting database state is confirmed; the exact action or background process that produced it has not been established. Source inspection used main, not an exact build checkout.
Steps to reproduce
A deterministic reproduction has not yet been established. The observed sequence was:
- Use an existing Claude thread over multiple turns.
- Continue the thread later. Its earlier messages remain visible, but Claude lacks their context.
- Fork from a response before the context loss.
- Send a message in the fork. Dispatch fails.
- Locate the imported thread associated with the original native session and attempt to continue it. That attempt also fails.
It is unknown whether importing was explicitly initiated or performed automatically.
Version
Affected desktop app: 0.0.46-nightly.20261008.2819. The separately invoked triage CLI reported 0.0.45. The affected desktop logs and database used the nightly version and statev2.sqlite.
Environment
macOS, Darwin 25.5.0, arm64; T3 Code Nightly desktop app with local server; provider claudeAgent; model claude-opus-5-5; triage Node.js v26.8.2.
Evidence
Identifiers below are synthetic.
Pending fork transfer <TRANSFER_ID> has no resolvable source provider thread.
Continuing the imported thread produced:
ProjectionStoreApplyEventError:
Failed to apply orchestration projection event provider-turn.updated.
UNIQUE constraint failed:
orchestration_v2_projection_provider_turns.provider_thread_id,
orchestration_v2_projection_provider_turns.ordinal
The original thread retained 80 message records. The imported thread retained 75. The original native Claude session file also existed.Related issues
#2256 describes a similar visible-history/context-loss symptom, but does not establish this provider-association collision or the accompanying fork and uniqueness failures. No confirmed duplicate was identified.
Fix applied or workaround
Backed up the native Claude session and exported the original thread’s 80 saved messages. A fresh Claude thread read the complete transcript and successfully summarized the previous decisions and outstanding work.
This recovered written context without directly editing the database. It did not repair the original thread association or restore internal reasoning/tool state.
Filed by
Prepared by GPT-6 Astra in Codex via t3 triage.
Activity
juliusmarminge commentedon Oct 8, 2026
Note
Grok responding on behalf of Julius.
Thanks for the detailed write-up. The synthetic DB state you describe lines up with what main does today (checked at
a6ec88f7a). The likely cause is that the bulk importer builds the same provider-thread id the live Claude adapter already used for that session, and the projection upsert then moves the record over to the imported thread.How
Plikely changed ownerimport:<instance>:<session>,apps/server/src/project/AgentSessionImporter.ts:226-228) only checks for an existing thread under thatimport:id (:252-266). It never checks whether a native thread already owns the session. That's the gap tracked in Imported agent sessions duplicate T3-native threads: import dedup never consults the native thread namespace #10933.AgentSessionImporter.ts:270-273). The native Claude adapter derives it the same way (Adapters/ClaudeAdapterV2.ts:1113-1116), so both produce the sameP(IdAllocator.ts:216-232). By contrast, the per-session import path includesproviderInstanceId(Orchestrator.ts:2241-2245), so it shouldn't collide.provider-thread.updatedwithappThreadIdset to the imported thread (AgentSessionImporter.ts:312-318,:352-375). The projection upsert isON CONFLICT(provider_thread_id) DO UPDATE SET thread_id = excluded.thread_id, …(orchestration-v2/ProjectionStore.ts:2193-2206), soP'sthread_id/payload_jsonsilently switch toimport:claudeAgent:…. That matches what you saw.agentSessions.importseems to be the welcome wizard's import step (apps/web/src/components/onboarding/WelcomeWizard.tsx:1215), and visiting/welcomereopens it (apps/web/src/routes/welcome.tsx:13). So my best guess is that onboarding or import was re-run for a project that already had native Claude threads. I haven't confirmed that.How that explains the three symptoms
thread_id(ProjectionStore.ts:3020-3038). ThreadAstill hasactiveProviderThreadId = P, butPno longer appears in its projection, soactiveProviderThreadis undefined (Orchestrator.ts:4842-4844). The next send then mints a newpending:<runId>provider thread (Orchestrator.ts:5221-5226), which means a new native Claude conversation with no handoff.providerThreadForRunsearches only that same per-thread list (Orchestrator.ts:640-647). For run 19 it returns undefined, which trips the guard atOrchestrator.ts:5569-5574.providerTurns(ProviderTurnStartService.ts:1248-1254). The imported thread has none underP, so it picks ordinal 1, butA's turns already hold(P, 1…). That conflicts with the unique index on(provider_thread_id, ordinal)(persistence/Migrations/055_OrchestrationV2.ts:178).Origin: the v2 importer that emits this deterministic id arrived with the new orchestrator in #2829. The v1 importer from #5362 created duplicates (#10933) but didn't share provider-thread records.
Related work
providerInstanceIdto the importer's derived id. Its own description says it doesn't repair existing rows, so affected databases would still need a repair step.Possible fix directions (untested)
thread_idon an existing provider thread that other threads' runs still reference.Pback to the thread whose runs reference it, and detaches or retires the colliding imported thread.Workaround (not verified): don't continue or fork the imported thread, since that keeps hitting the unique constraint. I haven't found an in-app action that hands
Pback toA. A projection rebuild would most likely replay the import event afterA's events and land in the same state. For now, your approach (export the transcript and have a fresh thread read it) seems to be the safest way to recover.