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

Missing --objective.

Error message

Missing --objective.

What it means

The add-goal subcommand requires --objective (trimmed non-empty) alongside --title; the objective describes the measurable completion criterion for the new goal. Missing or whitespace-only objective aborts before any state is written.

Source

Thrown at src/cli/ultragoal.ts:439

          reason: reconciliation.snapshot.unavailableReason,
          message: 'Codex goal DB/schema/context is unavailable; artifact-backed Ultragoal status remains available, but strict Codex goal completion reconciliation is deferred.',
        }
        : undefined;
      if (json) printJson({ plan, summary: summarizeUltragoalPlan(plan), reconciliation, codexGoalFallback });
      else {
        printStatus(plan);
        if (codexGoalFallback) console.log(`codex goal fallback: ${codexGoalFallback.message}`);
        if (reconciliation && !reconciliation.ok) console.log(`codex goal warning: ${formatCodexGoalReconciliation(reconciliation)}`);
        else if (reconciliation?.warnings.length) console.log(`codex goal warning: ${formatCodexGoalReconciliation(reconciliation)}`);
      }
      return;
    }

    if (command === 'add-goal') {
      const title = readValue(rest, '--title');
      const objective = readValue(rest, '--objective');
      if (!title?.trim()) throw new UltragoalError('Missing --title.');
      if (!objective?.trim()) throw new UltragoalError('Missing --objective.');
      const result = await addUltragoalGoal(cwd, { title, objective, evidence: readValue(rest, '--evidence') });
      if (json) printJson({ ok: true, plan: result.plan, addedGoal: result.goal, summary: summarizeUltragoalPlan(result.plan) });
      else {
        console.log(`ultragoal added goal: ${result.goal.id}`);
        printStatus(result.plan);
      }
      return;
    }

    if (command === 'steer') {
      const proposalText = readValue(rest, '--proposal');
      const source = readValue(rest, '--source');
      if (proposalText) {
        const parsed = assertPlainObject(await readJsonInput(proposalText, '--proposal'), '--proposal');
        const proposal = normalizeSteeringProposal({ ...parsed, source: source ?? parsed.source }, proposalText);
        const result = await steerUltragoal(cwd, proposal, { directiveText: proposalText });
        printSteerResult(proposal, result, json);
        if (!result.accepted) process.exitCode = 1;

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Add `--objective "..."` describing the done-criteria
  2. Guard scripts with `--objective "${OBJ:?set OBJ}"`

Example fix

# before
omx ultragoal add-goal --title 'DB migration'
# Error: Missing --objective.

# after
omx ultragoal add-goal --title 'DB migration' --objective 'schema v2 migrated with zero-downtime cutover'
Defensive patterns

Strategy: validation

Validate before calling

if (!objective?.trim()) { console.error('--objective required'); process.exit(2); }

Type guard

function isNonEmptyString(v: string | undefined | null): v is string {
  return typeof v === 'string' && v.trim().length > 0;
}

Prevention

When it happens

Trigger: `omx ultragoal add-goal --title 'X'` without --objective, or `--objective ""` / whitespace-only value.

Common situations: Splitting title/objective mentally into one field; empty variable expansion in scripts; interactive drafting where objective was left for later.

Related errors


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