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

  1. Inspect/repair or delete the malformed autopilot state file at the path from resolveSeedStateFilePath(stateDir, 'autopilot', sessionId) so a fresh state can be rebuilt
  2. Check for concurrent processes writing the same session state and serialize access
  3. Restore from backup or re-seed the session state if the file is unrecoverable
  4. 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

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

Related errors


AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27). Data as JSON: /api/errors/74c59bcc66ab5297. Report an issue: GitHub.