Yeachan-Heo/oh-my-codex · error · AutoresearchGoalError

Autoresearch goal ${mission.slug} is already complete; creat

Error message

Autoresearch goal ${mission.slug} is already complete; create a new goal or explicitly reopen via a future workflow before recording more verdicts.

What it means

recordAutoresearchGoalVerdict refuses to record new verdicts once the mission's status is 'complete'. This is a terminal-state guard: a completed goal is immutable by design, and further verdicts require a new goal or an explicit future reopen workflow.

Source

Thrown at src/autoresearch/goal.ts:201

    throw new AutoresearchGoalError(`Invalid autoresearch-goal mission at ${repoRelative(cwd, path)}.`);
  }
  return parsed;
}

export async function readAutoresearchGoalCompletion(cwd: string, slug: string): Promise<AutoresearchGoalCompletion | null> {
  const path = autoresearchGoalCompletionPath(cwd, slugifyMissionName(slug));
  if (!existsSync(path)) return null;
  const parsed = JSON.parse(await readFile(path, 'utf-8')) as AutoresearchGoalCompletion;
  return parsed;
}

export async function recordAutoresearchGoalVerdict(
  cwd: string,
  options: RecordAutoresearchGoalVerdictOptions,
): Promise<{ mission: AutoresearchGoalMission; completion: AutoresearchGoalCompletion }> {
  const mission = await readAutoresearchGoal(cwd, options.slug);
  if (mission.status === 'complete') {
    throw new AutoresearchGoalError(`Autoresearch goal ${mission.slug} is already complete; create a new goal or explicitly reopen via a future workflow before recording more verdicts.`);
  }
  const evidence = requireText(options.evidence, '--evidence');
  const now = iso(options.now);
  const completion: AutoresearchGoalCompletion = {
    schema_version: 1,
    slug: mission.slug,
    verdict: options.verdict,
    passed: options.verdict === 'pass',
    summary: options.summary?.trim() || evidence,
    evidence,
    ...(options.artifactPath?.trim() ? { artifact_path: options.artifactPath.trim() } : {}),
    ...(mission.critic_command ? { critic_command: mission.critic_command } : {}),
    recorded_at: now,
  };

  mission.status = options.verdict === 'pass' ? 'passed' : options.verdict === 'fail' ? 'failed' : 'blocked';
  mission.updated_at = now;
  await writeMission(cwd, mission);

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Check mission.status before recording and branch your automation to stop or start a new goal
  2. Create a new autoresearch goal with `omx autoresearch-goal create` if more verdicts are genuinely needed
  3. If the completion was premature, manually revert status in the mission file only if no supported reopen workflow exists and you accept the risk
  4. Make your recording step idempotent by first reading the mission and skipping when complete

Example fix

// before
await recordAutoresearchGoalVerdict(repoRoot, { slug, evidence }); // throws if status === 'complete'
// after
const mission = await readAutoresearchGoal(repoRoot, slug);
if (mission.status !== 'complete') {
  await recordAutoresearchGoalVerdict(repoRoot, { slug, evidence });
}
Defensive patterns

Strategy: validation

Validate before calling

const mission = await readAutoresearchGoal(cwd, slug);
if (mission.status === 'complete') {
  console.log('goal already complete; skipping verdict recording');
  process.exit(0);
}

Type guard

function isMissionComplete(m: { status?: string }): boolean {
  return m.status === 'complete';
}

Try / catch

try {
  await recordAutoresearchGoalVerdict(cwd, options);
} catch (e) {
  if (e instanceof AutoresearchGoalError && e.message.includes('already complete')) return; // idempotent skip
  throw e;
}

Prevention

When it happens

Trigger: Calling recordAutoresearchGoalVerdict(cwd, { slug, ... }) for a mission whose JSON has status === 'complete' — typically after completeAutoresearchGoal already succeeded or the status was set manually.

Common situations: Pipelines that keep iterating after a goal completes, re-running an automation step that records verdicts, or scripts that do not check mission status before recording.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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