Yeachan-Heo/oh-my-codex · error · UltragoalError
Cannot record a ${options.status} checkpoint for ${goal.id}
Error message
Cannot record a ${options.status} checkpoint for ${goal.id} after the aggregate ultragoal plan is complete; the terminal aggregate receipt is immutable. What it means
The aggregate ultragoal plan already has aggregateCompletion.status === 'complete', and you attempted to record a non-'complete' checkpoint. The terminal aggregate receipt is treated as immutable, so no further blocked/failed/in_progress checkpoints may be recorded after aggregate completion.
Source
Thrown at src/ultragoal/artifacts.ts:1790
next.failedAt = undefined;
next.failureReason = undefined;
clearGoalBlockerFields(next);
next.updatedAt = now;
plan.activeGoalId = next.id;
plan.updatedAt = now;
await writePlan(cwd, plan);
await appendLedger(cwd, { ts: now, event: 'goal_started', goalId: next.id, status: next.status, message: `Attempt ${next.attempt}` });
return { plan, goal: next, resumed: false, done: false };
});
}
export async function checkpointUltragoal(cwd: string, options: CheckpointOptions): Promise<UltragoalPlan> {
return withUltragoalMutationLock(cwd, async () => {
const plan = await readUltragoalPlanUnderLock(cwd);
const goal = plan.goals.find((candidate) => candidate.id === options.goalId);
if (!goal) throw new UltragoalError(`Unknown ultragoal id: ${options.goalId}`);
if (plan.aggregateCompletion?.status === 'complete' && options.status !== 'complete') {
throw new UltragoalError(`Cannot record a ${options.status} checkpoint for ${goal.id} after the aggregate ultragoal plan is complete; the terminal aggregate receipt is immutable.`);
}
const now = iso(options.now);
if (options.status === 'blocked') {
if (goal.status !== 'in_progress') {
throw new UltragoalError(`Cannot record a blocked checkpoint for ${goal.id} while it is ${goal.status}; start or resume the ultragoal before recording a non-terminal blocker.`);
}
const snapshot = options.codexGoal === undefined ? null : parseCodexGoalSnapshot(options.codexGoal);
if (snapshot?.unavailableReason === 'db_schema_context_error') {
goal.updatedAt = now;
goal.failureReason = assertNonEmpty(options.evidence, '--evidence');
plan.activeGoalId = goal.id;
plan.updatedAt = now;
await writePlan(cwd, plan);
await appendLedger(cwd, {
ts: now,
event: 'goal_blocked',
goalId: goal.id,
status: goal.status,View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Do not record further non-complete checkpoints — the plan is finalized
- If the late checkpoint is legitimately needed, it must be recorded before final completion or not at all
- Audit your orchestration to cancel pending checkpoint jobs once aggregate completion is written
Defensive patterns
Strategy: type-guard
Validate before calling
const plan = await readUltragoalPlan(cwd); if (plan.aggregateCompletion?.status === 'complete' && options.status !== 'complete') skip();
Type guard
const isPlanFinalized = (plan: UltragoalPlan) => plan.aggregateCompletion?.status === 'complete';
Prevention
- Cancel pending checkpoint jobs once aggregate completion is written
- Treat the aggregate receipt as append-nothing: design pipelines to stop after finalization
When it happens
Trigger: Calling checkpointUltragoal with options.status of 'blocked', 'failed', or similar after plan.aggregateCompletion.status is 'complete' and the status being recorded is not 'complete'.
Common situations: Retrying an old blocked checkpoint after the final goal completed the plan; automated retry loops firing late; concurrent pipelines recording checkpoints after finalization.
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
- Cannot add a goal to an already completed aggregate ultragoa
- Unknown ultragoal id: ${options.goalId}
- Cannot record a blocked checkpoint for ${goal.id} while it i
- Autoresearch goal ${mission.slug} is already complete; creat
- autoresearch_resume_terminal_run
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/d95c38c0b7a55aec.
Report an issue: GitHub.