{"record":{"id":"a1b15d1487456cde","repo":"Yeachan-Heo/oh-my-codex","slug":"autopilot-handoff-session-id-does-not-match-the-se","errorCode":null,"errorMessage":"Autopilot handoff session_id does not match the selected session.","messagePattern":"Autopilot handoff session_id does not match the selected session\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/cli/autopilot.ts","lineNumber":57,"sourceCode":"  const valueFlags = new Set(['--task', '--session', '--to', '--handoff-json']);\n  const words: string[] = [];\n  for (let i = 0; i < args.length; i += 1) {\n    if (valueFlags.has(args[i])) { i += 1; continue; }\n    if (!args[i].startsWith('--')) words.push(args[i]);\n  }\n  return words.join(' ').trim();\n}\n\nasync function jsonInput(raw: string): Promise<Record<string, unknown>> {\n  const text = raw.trim().startsWith('{') ? raw : await readFile(raw, 'utf-8');\n  const parsed = JSON.parse(text) as unknown;\n  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error('--handoff-json must resolve to a JSON object.');\n  return parsed as Record<string, unknown>;\n}\n\nfunction assertBoundHandoffIdentity(handoff: Record<string, unknown>, cwd: string, sessionId?: string): void {\n  if (typeof handoff.session_id === 'string' && handoff.session_id !== sessionId) {\n    throw new Error('Autopilot handoff session_id does not match the selected session.');\n  }\n  if (typeof handoff.workingDirectory === 'string' && handoff.workingDirectory !== cwd) {\n    throw new Error('Autopilot handoff workingDirectory does not match the selected workspace.');\n  }\n  handoff.session_id = sessionId;\n  handoff.workingDirectory = cwd;\n}\n\nasync function readAutopilot(cwd: string, sessionId?: string) {\n  return sessionId\n    ? readModeStateForExplicitSession('autopilot', sessionId, cwd)\n    : readModeState('autopilot', cwd);\n}\n\n/**\n * A terminalization that carried skipped gates must never read as clean success. The durable\n * marker is the machine token `complete-with-skipped-gates`; this renders the human string and\n * names each skipped gate with its missing evidence so the report is actionable.","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/cli/autopilot.ts#L39-L75","documentation":"Thrown by assertBoundHandoffIdentity() during `autopilot advance` when the handoff JSON contains a session_id string that differs from the currently selected OMX session. This is a safety check that prevents a handoff produced in one session from being applied to a different session.","triggerScenarios":"Calling autopilot advance with a handoff payload whose session_id field (a string) does not equal the sessionId of the current session (as resolved from cwd/session selection). Undefined or non-string session_id values pass the check and are overwritten with the current sessionId.","commonSituations":"Replaying a handoff JSON captured from an earlier run after the session was recreated, running the advance command in a fresh shell where a different session is selected, or hand-editing a handoff file and leaving a stale session_id.","solutions":["Regenerate the handoff from the current session (the session that will consume it) so session_id matches","Remove or null out the session_id field in the handoff JSON — the check only applies to string values, and assertBoundHandoffIdentity will stamp the correct current sessionId","Verify the selected session with the session status command before advancing and switch to the session the handoff was created for"],"exampleFix":"// before\n{\"session_id\":\"sess-old\",\"task\":\"...\"}\n// after\n{\"task\":\"...\"}  // session_id gets stamped from the current session","handlingStrategy":"validation","validationCode":"const h = JSON.parse(fs.readFileSync(handoffPath,'utf-8'));\nif (typeof h.session_id === 'string' && h.session_id !== currentSessionId) {\n  delete h.session_id; // let CLI stamp the current session\n  fs.writeFileSync(handoffPath, JSON.stringify(h));\n}","typeGuard":null,"tryCatchPattern":"try { await advance(handoff); } catch (e) { if (e.message.includes('session_id does not match')) regenerateHandoffFromCurrentSession(); else throw e; }","preventionTips":["Always produce and consume handoffs within the same session","Omit session_id from handoff templates so the CLI binds it automatically"],"tags":["cli","session-mismatch","handoff","autopilot"],"backgroundTag":"session-id-mismatch","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}