{"record":{"id":"74c59bcc66ab5297","repo":"Yeachan-Heo/oh-my-codex","slug":"cannot-advance-supervised-autopilot-child-phase-a","errorCode":null,"errorMessage":"Cannot advance supervised Autopilot child phase: autopilot detail state is malformed","messagePattern":"Cannot advance supervised Autopilot child phase: autopilot detail state is malformed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/hooks/keyword-detector.ts","lineNumber":3544,"sourceCode":"    ...(isAutopilotSuccessfulTerminalState(state) ? { completion_status: 'complete-with-skipped-gates' } : {}),\n  };\n}\n\n// Mirror the `state_write` backend completion contract: a keyword-driven\n// Autopilot phase advance is permitted, while any missing gate evidence is\n// recorded as a visible advisory on the detail state.\nasync function resolveAutopilotSupervisedChildPhaseState(\n  stateDir: string,\n  sessionId: string | undefined,\n  childSkill: string,\n): Promise<string> {\n  const { absolutePath } = resolveSeedStateFilePath(stateDir, 'autopilot', sessionId);\n  const existingResult = await readJsonStateWithStatus(absolutePath);\n  const existing = existingResult.state;\n  const existingMode = safeString(existing?.mode).trim();\n\n  if (existingResult.status === 'malformed') {\n    throw new Error('Cannot advance supervised Autopilot child phase: autopilot detail state is malformed');\n  }\n  if (existing && existingMode !== 'autopilot') {\n    throw new Error(`Cannot advance supervised Autopilot child phase: expected autopilot detail state, found ${existingMode || 'unknown'}`);\n  }\n  // PREFLIGHT, before the caller reconciles child projections. The commit-time assertion in\n  // persistAutopilotSupervisedChildPhaseState already blocks the parent advance, but it runs after\n  // reconcileWorkflowTransition may have written a stale active child, leaving the refused operation\n  // half-applied. Rejecting here keeps it all-or-nothing; the later assertion stays because that\n  // function rereads the parent, so it is the TOCTOU revalidation rather than a duplicate.\n  assertValidHandoffCarriersIn((existing ?? {}) as Record<string, unknown>, 'stored autopilot');\n\n  return childSkill;\n}\n\nasync function persistAutopilotSupervisedChildPhaseState(\n  cwd: string,\n  stateDir: string,\n  sessionId: string | undefined,","sourceCodeStart":3526,"sourceCodeEnd":3562,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/hooks/keyword-detector.ts#L3526-L3562","documentation":"Thrown during a preflight check before advancing a supervised Autopilot child phase. The per-session autopilot detail state file (resolved via resolveSeedStateFilePath) could be read but failed JSON/schema parsing, so readJsonStateWithStatus returned status 'malformed'. The library refuses to advance the parent phase on top of corrupt persisted state to keep the transition all-or-nothing.","triggerScenarios":"Calling the supervised Autopilot child-phase advance path when sessions/<stateDir>/autopilot/<sessionId>.json exists but is truncated, hand-edited, or contains invalid JSON, so readJsonStateWithStatus returns 'malformed'.","commonSituations":"A previous process crashed mid-write leaving a partial JSON file; manual editing of state files; disk-full truncation; concurrent writers corrupting the file; version migrations that left half-written state.","solutions":["Inspect/repair or delete the malformed autopilot state file at the path from resolveSeedStateFilePath(stateDir, 'autopilot', sessionId) so a fresh state can be rebuilt","Check for concurrent processes writing the same session state and serialize access","Restore from backup or re-seed the session state if the file is unrecoverable","Report/file a bug if corruption recurs — it may indicate a non-atomic write path"],"exampleFix":"// before: advancing on possibly corrupt state\nawait advanceSupervisedAutopilotChildPhase(stateDir, sessionId, nextState);\n\n// after: pre-check and rebuild when malformed\nconst { absolutePath } = resolveSeedStateFilePath(stateDir, 'autopilot', sessionId);\nconst res = await readJsonStateWithStatus(absolutePath);\nif (res.status === 'malformed') {\n  await rm(absolutePath, { force: true }); // let the writer rebuild fresh state\n}\nawait advanceSupervisedAutopilotChildPhase(stateDir, sessionId, nextState);","handlingStrategy":"validation","validationCode":"const { absolutePath } = resolveSeedStateFilePath(stateDir, 'autopilot', sessionId);\nconst res = await readJsonStateWithStatus(absolutePath);\nif (res.status === 'malformed') {\n  // rebuild or abort before advancing\n  await rm(absolutePath, { force: true });\n}","typeGuard":"function isParsableAutopilotState(res: ReturnType<typeof readJsonStateWithStatus extends Promise<infer R> ? R : never>): boolean {\n  return res.status !== 'malformed';\n}","tryCatchPattern":"try { await advanceChildPhase(...); } catch (e) { if (e instanceof Error && e.message.includes('autopilot detail state is malformed')) { /* rebuild state and retry once */ } else throw e; }","preventionTips":["Never hand-edit session state JSON files","Ensure only one process writes a session's state directory","Keep backups of state dirs so corrupt files can be restored"],"tags":["autopilot","state-corruption","preflight-check","json-state"],"backgroundTag":"corrupt-state-file","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}