Yeachan-Heo/oh-my-codex · error · MissionCommandError
Mission execution requires a task runner; use status for rea
Error message
Mission execution requires a task runner; use status for read-only inspection.
What it means
For mission resume/rerun, the command was invoked without a runTask function in options. Execution actions require a caller-supplied task runner; only status is available without one.
Source
Thrown at src/cli/mission.ts:400
task.status = parsed.markStatus ?? task.status;
task.completed_at = now().toISOString();
delete task.exit_code;
syncSummary(summary, { status: missionStatus(summary.tasks) });
await persistSummary(paths.summaryPath, summary);
await appendLedger(paths.ledgerPath, { event: "task_marked", at: task.completed_at, slug: summary.slug, task_id: task.id, status: task.status });
if (parsed.json) stdout(JSON.stringify({ ok: true, summary_path: paths.summaryPath, ledger_path: paths.ledgerPath, summary }, null, 2));
else {
stdout(`mission task marked ${task.status}: ${summary.slug} ${task.id}`);
stdout(`summary: ${paths.summaryPath}`);
stdout(`ledger: ${paths.ledgerPath}`);
}
return;
}
if (parsed.action === "resume" || parsed.action === "rerun") {
const summary = await readSummary(paths.summaryPath);
const runTask = options.runTask;
if (!runTask) throw new MissionCommandError("Mission execution requires a task runner; use status for read-only inspection.");
if (parsed.action === "rerun" && !summary.tasks.some((task) => task.id === parsed.taskId)) {
throw new MissionCommandError(`No mission task found for --task ${parsed.taskId}.`);
}
summary.input_path = paths.inputPath;
summary.dry_run = false;
summary.continue_on_error = parsed.continueOnError;
summary.codex_args = parsed.codexArgs;
summary.status = "running";
delete summary.completed_at;
for (const task of summary.tasks) {
if (task.status === "running") task.status = "pending";
}
syncSummary(summary, { status: "running" });
await persistSummary(paths.summaryPath, summary);
await appendLedger(paths.ledgerPath, { event: parsed.action === "resume" ? "mission_resumed" : "mission_rerun_started", at: now().toISOString(), slug: summary.slug, summary_path: paths.summaryPath, task_id: parsed.taskId });
const shouldRun = parsed.action === "resume"View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Pass options.runTask: a function that executes a task and returns its exit code
- Use mission status when no runner is available
- For plan-only validation, use run --dry-run with a runner or the parser directly
Example fix
// before
await missionCommand(['resume', 'mission.md'], { stdout });
// after
await missionCommand(['resume', 'mission.md'], { stdout, runTask: async (task) => spawnRunner(task) }); Defensive patterns
Strategy: validation
Validate before calling
if ((action === 'resume' || action === 'rerun') && typeof options.runTask !== 'function') {
failEarly('no task runner configured');
} Type guard
const hasRunner = (o: MissionOptions): o is MissionOptions & { runTask: RunTask } =>
typeof o.runTask === 'function'; Prevention
- Wire runTask at application bootstrap
- Guard read-only embeds to only expose status
When it happens
Trigger: Calling missionCommand(['resume', file], {}) or with options.runTask undefined — e.g. embedding the CLI in a host that only wires up read-only inspection.
Common situations: Library embedding where the host deliberately omits the runner; refactoring that dropped the runTask option; tests invoking missionCommand without a stub runner.
Related errors
- Mission execution requires a task runner; use --dry-run for
- No autoresearch-goal mission found at ${repoRelative(cwd, pa
- Missing value for ${flag}.
- --dry-run is only supported for mission run/plan.
- --continue-on-error is not supported for mission status.
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/48bfa4dd79c5c7ba.
Report an issue: GitHub.