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

Missing --goal-id.

Error message

Missing --goal-id.

What it means

record-review-blockers records final-review blockers against an existing goal, so --goal-id is mandatory to know which goal to block. It is the first of four required flags checked (--goal-id, --title, --objective, --evidence) before recordFinalReviewBlockers runs.

Source

Thrown at src/cli/ultragoal.ts:475

        if (!result.accepted) process.exitCode = 1;
        return;
      }
      const proposal = await parseSteeringProposal(rest);
      if (!proposal.evidence?.trim?.()) throw new UltragoalError('Missing --evidence.');
      if (!proposal.rationale?.trim?.()) throw new UltragoalError('Missing --rationale.');
      proposal.source = parseSteeringSource(source, proposal.source);
      const result = await steerUltragoal(cwd, proposal, { directiveText: buildSteerDirectiveText(rest) });
      printSteerResult(proposal, result, json);
      if (!result.accepted) process.exitCode = 1;
      return;
    }

    if (command === 'record-review-blockers') {
      const goalId = readValue(rest, '--goal-id');
      const title = readValue(rest, '--title');
      const objective = readValue(rest, '--objective');
      const evidence = readValue(rest, '--evidence');
      if (!goalId) throw new UltragoalError('Missing --goal-id.');
      if (!title?.trim()) throw new UltragoalError('Missing --title.');
      if (!objective?.trim()) throw new UltragoalError('Missing --objective.');
      if (!evidence?.trim()) throw new UltragoalError('Missing --evidence.');
      const codexGoal = await parseCodexGoalJson(readValue(rest, '--codex-goal-json'));
      const result = await recordFinalReviewBlockers(cwd, { goalId, title, objective, evidence, codexGoal });
      if (json) printJson({ ok: true, plan: result.plan, blockedGoal: result.blockedGoal, addedGoal: result.addedGoal, summary: summarizeUltragoalPlan(result.plan) });
      else {
        console.log(`ultragoal final review blockers recorded: ${result.blockedGoal.id} -> review_blocked; added ${result.addedGoal.id}`);
        printStatus(result.plan);
      }
      return;
    }

    if (command === 'complete' || command === 'complete-goals' || command === 'next' || command === 'start-next') {
      const result = await startNextUltragoal(cwd, { retryFailed: hasFlag(rest, '--retry-failed') });
      if (!result.goal) {
        const handoff = blockedDecisionHandoff(result.plan);
        if (json) {

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Get the id via `omx ultragoal status` (or --json and jq '.plan.goals[].id') and pass `--goal-id <id>`
  2. Quote the id: --goal-id "$GOAL_ID" and assert it is set in scripts

Example fix

# before
omx ultragoal record-review-blockers --title t --objective o --evidence e
# Error: Missing --goal-id.

# after
omx ultragoal record-review-blockers --goal-id goal-3 --title t --objective o --evidence e
Defensive patterns

Strategy: validation

Validate before calling

if (!goalId) { console.error('--goal-id required; get one from `omx ultragoal status --json`'); process.exit(2); }

Type guard

function isGoalId(v: string | undefined): v is string {
  return !!v && /^goal(-\w+)?$/.test(v.trim()); // adjust to your id format
}

Prevention

When it happens

Trigger: `omx ultragoal record-review-blockers --title t --objective o --evidence e` with no --goal-id.

Common situations: Assuming the CLI infers the current goal from context (`status`/`next`); id copied from JSON but stripped by shell quoting; goal id renamed between versions.

Related errors


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