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

Invalid --source: ${raw}. Expected one of ${Array.from(STEER

Error message

Invalid --source: ${raw}. Expected one of ${Array.from(STEERING_SOURCES).join(', ')}.

What it means

--source on `omx ultragoal steer` labels where the steering proposal came from and must be one of the ULTRAGOAL_STEERING_SOURCES enum (default 'cli' when omitted). parseSteeringSource throws for any unrecognized string; the accepted list is embedded in the message.

Source

Thrown at src/cli/ultragoal.ts:230

    cwd: scope.cwd,
    sessionId: scope.sessionId,
  });
}

const STEERING_KINDS = new Set<UltragoalSteeringMutationKind>(ULTRAGOAL_STEERING_MUTATION_KINDS);
const STEERING_SOURCES = new Set<UltragoalSteeringSource>(ULTRAGOAL_STEERING_SOURCES);

type CliSteerResult = Awaited<ReturnType<typeof steerUltragoal>>;

function parseSteeringKind(raw: string | undefined): UltragoalSteeringMutationKind {
  if (!raw) throw new UltragoalError('Missing --kind for structured ultragoal steer.');
  if (!STEERING_KINDS.has(raw as UltragoalSteeringMutationKind)) throw new UltragoalError(`Invalid --kind: ${raw}. Expected one of ${Array.from(STEERING_KINDS).join(', ')}.`);
  return raw as UltragoalSteeringMutationKind;
}

function parseSteeringSource(raw: string | undefined, fallback: UltragoalSteeringSource = 'cli'): UltragoalSteeringSource {
  if (!raw) return fallback;
  if (!STEERING_SOURCES.has(raw as UltragoalSteeringSource)) throw new UltragoalError(`Invalid --source: ${raw}. Expected one of ${Array.from(STEERING_SOURCES).join(', ')}.`);
  return raw as UltragoalSteeringSource;
}

function assertPlainObject(value: unknown, label: string): Record<string, unknown> {
  if (!value || typeof value !== 'object' || Array.isArray(value)) throw new UltragoalError(`${label} must be a JSON object.`);
  return value as Record<string, unknown>;
}

function normalizeTargetGoalId(raw: Record<string, unknown>): string | undefined {
  if (typeof raw.targetGoalId === 'string' && raw.targetGoalId.trim()) return raw.targetGoalId.trim();
  if (Array.isArray(raw.targetGoalIds)) return raw.targetGoalIds.find((id): id is string => typeof id === 'string' && id.trim().length > 0)?.trim();
  return undefined;
}

function normalizeSteeringProposal(raw: Record<string, unknown>, _fallbackDirectiveText?: string): UltragoalSteeringProposal {
  const kind = parseSteeringKind(typeof raw.kind === 'string' ? raw.kind : undefined);
  const source = parseSteeringSource(typeof raw.source === 'string' ? raw.source : undefined);
  const after = raw.after && typeof raw.after === 'object' && !Array.isArray(raw.after)

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Use one of the exact values printed in the error message (e.g. 'cli')
  2. Omit --source to get the default 'cli'
  3. Update to the latest version if you need a new source label that docs advertise

Example fix

# before
omx ultragoal steer --kind update_goal --source api ...
# Error: Invalid --source: api. Expected one of cli, agent.

# after
omx ultragoal steer --kind update_goal --source agent ...
Defensive patterns

Strategy: type-guard

Validate before calling

const SOURCES = new Set(ULTRAGOAL_STEERING_SOURCES as readonly string[]);
if (source && !SOURCES.has(source)) { console.error(`--source must be one of: ${[...SOURCES].join(', ')}`); process.exit(2); }

Type guard

function isSteeringSource(v: string, valid: ReadonlySet<string>): v is (typeof ULTRAGOAL_STEERING_SOURCES)[number] {
  return valid.has(v);
}

Prevention

When it happens

Trigger: Passing `--source api`, `--source codex`, or a typo'd value not in ULTRAGOAL_STEERING_SOURCES; correct values are typically 'cli', 'agent', etc. per the exported enum.

Common situations: Teams piping steering from automation assuming an arbitrary source label works; copy-paste from older docs where the enum was smaller; case sensitivity ('CLI' vs 'cli').

Related errors


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