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

Missing goal workflow objective.

Error message

Missing goal workflow objective.

What it means

GoalWorkflowError thrown by createGoalWorkflowRun when options.objective is empty after trimming. The objective is the required human-readable goal of the run; without it the workflow has nothing to drive slug generation and status tracking, so creation is refused before any files are written.

Source

Thrown at src/goal-workflows/artifacts.ts:128

  return join(goalWorkflowDir(cwd, workflow, slug), GOAL_WORKFLOW_STATUS);
}

export function goalWorkflowLedgerPath(cwd: string, workflow: string, slug: string): string {
  return join(goalWorkflowDir(cwd, workflow, slug), GOAL_WORKFLOW_LEDGER);
}

export async function appendGoalWorkflowLedger(cwd: string, run: GoalWorkflowRun, entry: GoalWorkflowLedgerEntry): Promise<void> {
  await mkdir(join(cwd, run.artifactDir), { recursive: true });
  await appendFile(join(cwd, run.ledgerPath), `${JSON.stringify(entry)}\n`);
}

async function writeRun(cwd: string, run: GoalWorkflowRun): Promise<void> {
  await mkdir(join(cwd, run.artifactDir), { recursive: true });
  await writeFile(join(cwd, run.statusPath), `${JSON.stringify(run, null, 2)}\n`);
}

export async function createGoalWorkflowRun(cwd: string, options: CreateGoalWorkflowRunOptions): Promise<GoalWorkflowRun> {
  if (!options.objective.trim()) throw new GoalWorkflowError('Missing goal workflow objective.');
  const workflow = normalizeGoalWorkflowSegment(options.workflow);
  const slug = normalizeGoalWorkflowSegment(options.slug ?? slugFromObjective(options.objective), 'goal-workflow');
  const statusPath = goalWorkflowStatusPath(cwd, workflow, slug);
  if (!options.force && existsSync(statusPath)) {
    throw new GoalWorkflowError(`Refusing to overwrite existing ${repoRelative(cwd, statusPath)}; pass force to recreate it.`);
  }

  const now = iso(options.now);
  const artifactDir = repoRelative(cwd, goalWorkflowDir(cwd, workflow, slug));
  const run: GoalWorkflowRun = {
    version: 1,
    workflow,
    slug,
    objective: options.objective.trim(),
    status: 'pending',
    createdAt: now,
    updatedAt: now,
    artifactDir,

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. Validate the objective is non-empty after trim before calling createGoalWorkflowRun.
  2. If the objective comes from config, fail fast with a descriptive error naming the missing key.
  3. Default or prompt for the objective in the calling layer when it would be blank.
  4. Add a form/schema-level required constraint so blank objectives never reach the API.

Example fix

// before
await createGoalWorkflowRun(cwd, { objective: cfg.objective ?? '' });

// after
const objective = (cfg.objective ?? '').trim();
if (!objective) throw new Error('config.objective is required to create a goal workflow run');
await createGoalWorkflowRun(cwd, { objective });
Defensive patterns

Strategy: validation

Validate before calling

const objective = options.objective?.trim();
if (!objective) throw new Error('objective is required');
await createGoalWorkflowRun(cwd, { ...options, objective });

Type guard

function isValidObjective(v: unknown): v is string {
  return typeof v === 'string' && v.trim().length > 0;
}

Try / catch

try { await createGoalWorkflowRun(cwd, options); }
catch (e) { if (e instanceof GoalWorkflowError && e.message.includes('objective')) {/* prompt user for objective */} else throw e; }

Prevention

When it happens

Trigger: Calling createGoalWorkflowRun with objective: '', ' ', or a variable that is undefined/null coerced to whitespace; CLI/form layer submitting an untouched objective field; objective loaded from an empty config key.

Common situations: Automation derives the objective from a template variable that wasn't filled in; YAML/JSON config has an empty objective key; whitespace-only input accepted by a UI text field; test fixture left blank.

Related errors


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