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

Invalid ultragoal plan at ${repoRelative(cwd, path)}.

Error message

Invalid ultragoal plan at ${repoRelative(cwd, path)}.

What it means

The ultragoal plan JSON file exists but does not match the expected schema: it must have version === 1 and a goals array. This is a validation guard run immediately after JSON.parse, so malformed, hand-edited, or wrong-version plan files are rejected.

Source

Thrown at src/ultragoal/artifacts.ts:992

async function appendLedger(cwd: string, entry: UltragoalLedgerEntry): Promise<void> {
  await mkdir(ultragoalDir(cwd), { recursive: true });
  const path = ultragoalLedgerPath(cwd);
  await appendFile(path, `${JSON.stringify(entry)}\n`);
}

/** Pure plan read: no durable writes, no migration. */
async function readUltragoalPlanFile(cwd: string): Promise<UltragoalPlan> {
  const path = ultragoalGoalsPath(cwd);
  let raw: string;
  try {
    raw = await readFile(path, 'utf-8');
  } catch {
    throw new UltragoalError(`No ultragoal plan found at ${repoRelative(cwd, path)}. Run \`omx ultragoal create-goals ...\` first.`);
  }
  const parsed = JSON.parse(raw) as UltragoalPlan;
  if (parsed.version !== 1 || !Array.isArray(parsed.goals)) {
    throw new UltragoalError(`Invalid ultragoal plan at ${repoRelative(cwd, path)}.`);
  }
  return parsed;
}

/**
 * Pure, lock-free plan read for read-only surfaces such as
 * `omx ultragoal status`: never migrates, never writes, and is therefore safe
 * for Team workers. Durable legacy migration only happens through
 * readUltragoalPlan or the mutators, under the mutation lock and authority gate.
 */
export async function readUltragoalPlanSnapshot(cwd: string): Promise<UltragoalPlan> {
  return readUltragoalPlanFile(cwd);
}

function requiresCanonicalStatePathMigration(plan: UltragoalPlan, statePathPrefix: string): boolean {
  return statePathPrefix !== '' && plan.codexObjective === ULTRAGOAL_AGGREGATE_CODEX_OBJECTIVE;
}

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Inspect the plan file at the path in the message: cat the JSON and confirm it has "version": 1 and a "goals": [...] array
  2. If the file was hand-edited or mangled, regenerate it with `omx ultragoal create-goals --force`
  3. If a version mismatch came from a tool upgrade, re-create the plan with the current tool version and re-apply goal changes
  4. If a custom script writes the plan, make it emit {version: 1, goals: [...]}

Example fix

// before (invalid plan file)
{ "version": 2, "objectives": [] }

// after
{ "version": 1, "goals": [ /* UltragoalItem[] */ ], "activeGoalId": null }
Defensive patterns

Strategy: type-guard

Validate before calling

const plan = JSON.parse(await readFile(p, 'utf-8'));
if (plan?.version !== 1 || !Array.isArray(plan.goals)) throw new Error('plan needs regeneration');

Type guard

function isValidUltragoalPlan(v: unknown): v is { version: 1; goals: unknown[] } { return !!v && typeof v === 'object' && (v as any).version === 1 && Array.isArray((v as any).goals); }

Try / catch

try { await readUltragoalPlan(cwd); } catch (e) { if (e instanceof UltragoalError && e.message.startsWith('Invalid ultragoal plan')) await createUltragoalPlan(cwd, { brief, force: true }); else throw e; }

Prevention

When it happens

Trigger: Calling a plan-reading API (readUltragoalPlanFile and everything built on it) when .ultragoal goals JSON has version other than 1, a missing/non-array `goals` field, or valid JSON of the wrong shape (e.g. a plan exported from a newer/older tool version).

Common situations: Hand-editing the plan JSON and dropping/renaming fields; upgrading or downgrading the tooling so the on-disk version field no longer matches; merging conflicts resolved incorrectly leaving a syntactically valid but structurally wrong file; writing a custom generator that omits `goals`.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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