{"record":{"id":"5e5337ca54cd4215","repo":"paperclipai/paperclip","slug":"thread-read-returned-a-different-driver-session","errorCode":null,"errorMessage":"thread/read returned a different driver session","messagePattern":"thread/read returned a different driver session","errorType":"error_code","errorClass":"HarnessReconciliationError","httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/drivers/codex/codex-history.ts","lineNumber":68,"sourceCode":"    }\n    cursors.add(next);\n    cursor = next;\n  }\n  throw new HarnessReconciliationError(\n    `codex_history_incomplete: ${method} page limit exceeded`,\n  );\n}\n\nexport async function readCodexThreadState(\n  transport: Requester,\n  threadId: string,\n): Promise<Record<string, unknown>> {\n  const snapshot = await transport.request(\"thread/read\", {\n    threadId,\n    includeTurns: false,\n  });\n  if (text(record(snapshot.thread).id) !== threadId)\n    throw new HarnessReconciliationError(\n      \"thread/read returned a different driver session\",\n    );\n  return snapshot;\n}\n\nexport function readCodexTurnMetadata(\n  transport: Requester,\n  threadId: string,\n): Promise<Record<string, unknown>[]> {\n  return pages(\n    transport,\n    \"thread/turns/list\",\n    { threadId, sortDirection: \"asc\", itemsView: \"notLoaded\" },\n    (value) => {\n      if (\n        ![\n          \"inProgress\",\n          \"completed\",","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/drivers/codex/codex-history.ts#L50-L86","documentation":"readCodexThreadState calls thread/read with a threadId and verifies the returned snapshot.thread.id matches. When the provider returns a snapshot for a different thread, the library throws a HarnessReconciliationError, because reconciling against the wrong session would produce false conclusions about running work.","triggerScenarios":"thread/read responds successfully but snapshot.thread.id !== the requested threadId — typically after a stale/closed thread id, a provider that reuses or recycles thread ids, or a transport multiplexing responses incorrectly.","commonSituations":"Recovery running against a restarted app-server whose thread store was reset; passing an old threadId cached from a previous session; provider bug returning a default/first thread instead of an error for unknown ids.","solutions":["Verify the threadId passed to recovery is the live id from the current session, not a cached value from before an app-server restart.","Restart recovery with a fresh thread read; if the thread no longer exists, recreate the session rather than reconciling.","Check the app-server version for thread/read misbehavior on unknown thread ids and upgrade.","Log both requested and returned ids to diagnose id recycling or transport muxing."],"exampleFix":"// before\nconst snapshot = await readCodexThreadState(transport, staleThreadId);\n// after\nconst currentThreadId = session.getThreadId();\nconst snapshot = await readCodexThreadState(transport, currentThreadId);","handlingStrategy":"validation","validationCode":"if (!threadId || typeof threadId !== 'string') throw new Error('threadId required and must be a live session id'); if (threadId !== session.getThreadId()) throw new Error('stale threadId: session was recreated');","typeGuard":"function isWrongThreadSnapshot(s: unknown, threadId: string): boolean { return (s as any)?.thread?.id !== threadId; }","tryCatchPattern":"try { return await readCodexThreadState(transport, threadId); } catch (e) { if (e instanceof HarnessReconciliationError && e.message.includes('different driver session')) { await recreateSession(); return null; } throw e; }","preventionTips":["Always source threadId from the live session, never a long-lived cache","Refresh thread ids after any app-server restart","Log requested vs returned thread ids on mismatch","Fail recovery closed rather than reconciling against another thread"],"tags":["codex","thread","identity","reconciliation"],"backgroundTag":"record-not-found","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}