Yeachan-Heo/oh-my-codex · error · Error
Cannot advance supervised Autopilot child phase: autopilot d
Error message
Cannot advance supervised Autopilot child phase: autopilot detail state is malformed
What it means
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.
Source
Thrown at src/hooks/keyword-detector.ts:3544
...(isAutopilotSuccessfulTerminalState(state) ? { completion_status: 'complete-with-skipped-gates' } : {}),
};
}
// Mirror the `state_write` backend completion contract: a keyword-driven
// Autopilot phase advance is permitted, while any missing gate evidence is
// recorded as a visible advisory on the detail state.
async function resolveAutopilotSupervisedChildPhaseState(
stateDir: string,
sessionId: string | undefined,
childSkill: string,
): Promise<string> {
const { absolutePath } = resolveSeedStateFilePath(stateDir, 'autopilot', sessionId);
const existingResult = await readJsonStateWithStatus(absolutePath);
const existing = existingResult.state;
const existingMode = safeString(existing?.mode).trim();
if (existingResult.status === 'malformed') {
throw new Error('Cannot advance supervised Autopilot child phase: autopilot detail state is malformed');
}
if (existing && existingMode !== 'autopilot') {
throw new Error(`Cannot advance supervised Autopilot child phase: expected autopilot detail state, found ${existingMode || 'unknown'}`);
}
// PREFLIGHT, before the caller reconciles child projections. The commit-time assertion in
// persistAutopilotSupervisedChildPhaseState already blocks the parent advance, but it runs after
// reconcileWorkflowTransition may have written a stale active child, leaving the refused operation
// half-applied. Rejecting here keeps it all-or-nothing; the later assertion stays because that
// function rereads the parent, so it is the TOCTOU revalidation rather than a duplicate.
assertValidHandoffCarriersIn((existing ?? {}) as Record<string, unknown>, 'stored autopilot');
return childSkill;
}
async function persistAutopilotSupervisedChildPhaseState(
cwd: string,
stateDir: string,
sessionId: string | undefined,View on GitHub (pinned to 3ad79a8a6f)
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
Example fix
// before: advancing on possibly corrupt state
await advanceSupervisedAutopilotChildPhase(stateDir, sessionId, nextState);
// after: pre-check and rebuild when malformed
const { absolutePath } = resolveSeedStateFilePath(stateDir, 'autopilot', sessionId);
const res = await readJsonStateWithStatus(absolutePath);
if (res.status === 'malformed') {
await rm(absolutePath, { force: true }); // let the writer rebuild fresh state
}
await advanceSupervisedAutopilotChildPhase(stateDir, sessionId, nextState); Defensive patterns
Strategy: validation
Validate before calling
const { absolutePath } = resolveSeedStateFilePath(stateDir, 'autopilot', sessionId);
const res = await readJsonStateWithStatus(absolutePath);
if (res.status === 'malformed') {
// rebuild or abort before advancing
await rm(absolutePath, { force: true });
} Type guard
function isParsableAutopilotState(res: ReturnType<typeof readJsonStateWithStatus extends Promise<infer R> ? R : never>): boolean {
return res.status !== 'malformed';
} Try / catch
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; } Prevention
- 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
When it happens
Trigger: 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'.
Common situations: 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.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Missing value for ${flag}.
- --handoff-json must resolve to a JSON object.
- Autopilot handoff session_id does not match the selected ses
- Autopilot handoff workingDirectory does not match the select
- Missing --task.
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/74c59bcc66ab5297.
Report an issue: GitHub.