Yeachan-Heo/oh-my-codex · warning · UltragoalError
Refusing to overwrite existing ${ULTRAGOAL_DIR}/${ULTRAGOAL_
Error message
Refusing to overwrite existing ${ULTRAGOAL_DIR}/${ULTRAGOAL_GOALS}; pass --force to recreate it. What it means
createUltragoalPlan refuses to silently replace an existing ultragoal plan artifact. Without options.force, an existing goals file is treated as user-owned state and the mutation-lock-guarded create aborts.
Source
Thrown at src/ultragoal/artifacts.ts:1066
if (!requiresAggregateObjectiveMigration(parsed, ultragoalStatePathPrefix(cwd))) return parsed;
// The legacy objective migration is a durable story transition: it requires
// writable lifecycle authority and the mutation lock, and re-reads the plan
// under the lock so a concurrent mutator cannot be clobbered or duplicated.
return withUltragoalMutationLock(cwd, async () => readUltragoalPlanUnderLock(cwd));
}
async function writePlan(cwd: string, plan: UltragoalPlan): Promise<void> {
await mkdir(ultragoalDir(cwd), { recursive: true });
const path = ultragoalGoalsPath(cwd);
const tmpPath = `${path}.${process.pid}.${Date.now()}.tmp`;
await writeFile(tmpPath, `${JSON.stringify(plan, null, 2)}\n`);
await rename(tmpPath, path);
}
export async function createUltragoalPlan(cwd: string, options: CreateUltragoalOptions): Promise<UltragoalPlan> {
return withUltragoalMutationLock(cwd, async () => {
if (!options.force && existsSync(ultragoalGoalsPath(cwd))) {
throw new UltragoalError(`Refusing to overwrite existing ${ULTRAGOAL_DIR}/${ULTRAGOAL_GOALS}; pass --force to recreate it.`);
}
const now = iso(options.now);
const sourceGoals: Array<{ title?: string; objective: string; tokenBudget?: number }> = options.goals?.length
? options.goals
: deriveGoalCandidates(options.brief);
const candidates = sourceGoals
.map((goal, index): UltragoalItem => ({
id: normalizeGoalId(goal.title ?? titleFromObjective(goal.objective, `Goal ${index + 1}`), index),
title: goal.title ?? titleFromObjective(goal.objective, `Goal ${index + 1}`),
objective: goal.objective.trim(),
status: 'pending',
tokenBudget: goal.tokenBudget,
attempt: 0,
createdAt: now,
updatedAt: now,
}));
const plan: UltragoalPlan = {View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Pass --force (options.force = true) if you intentionally want to recreate the plan
- If the existing plan is wanted, skip the create step (make the pipeline idempotent: check existence first)
- Delete or move the existing artifact only if you are sure it's disposable
- Audit CI scripts for unconditional create-goals invocations
Example fix
// before
await createUltragoalPlan(cwd, { brief }); // throws if plan exists
// after
await createUltragoalPlan(cwd, { brief, force: true }); Defensive patterns
Strategy: validation
Validate before calling
import { existsSync } from 'node:fs';
if (!existsSync(ultragoalGoalsPath(cwd))) { await createUltragoalPlan(cwd, { brief }); } Try / catch
try { await createUltragoalPlan(cwd, { brief }); } catch (e) { if (e instanceof UltragoalError && e.message.includes('pass --force')) { /* plan already exists; decide explicitly */ } else throw e; } Prevention
- Make create-goals steps idempotent: check existence before creating
- Only pass force when you accept losing the existing plan
- In CI, clean or cache the workspace deliberately so plan state is predictable
When it happens
Trigger: Calling createUltragoalPlan(cwd, {force: false, ...}) (the default) when .ultragoal/<ULTRAGOAL_GOALS> already exists — e.g. re-running `omx ultragoal create-goals` twice, or in CI where a previous step already created the plan.
Common situations: Re-running a setup script or pipeline stage that calls create-goals without an idempotency check; CI caching that leaves a stale plan in the workspace; two agents/processes racing to initialize a repo's plan.
Related errors
- Autoresearch goal ${mission.slug} is already complete; creat
- agent already exists: ${path}
- Invalid --codex-goal-mode; expected aggregate or per-story.
- Invalid ${label}: ${message}
- Missing --kind for structured ultragoal steer.
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/5f1577d2ae4538e4.
Report an issue: GitHub.