can1357/oh-my-pi · error · Error
cannot create a new goal because this session already has a
Error message
cannot create a new goal because this session already has a goal
What it means
Goal mode enforces one live goal per session. createGoal throws if the session already has a goal whose status is anything other than 'dropped' or 'complete' (e.g. active, budget-limited, or paused), because stacking goals would corrupt budget accounting.
Source
Thrown at packages/coding-agent/src/goals/runtime.ts:391
objective,
status: "active",
tokenBudget,
tokensUsed: 0,
timeUsedSeconds: 0,
createdAt: now,
updatedAt: now,
};
return { enabled: true, mode: "active", goal };
}
async createGoal(input: { objective: string; tokenBudget?: number }): Promise<GoalModeState> {
const objective = input.objective.trim();
if (!objective) throw new Error("objective is required when op=create");
validateTokenBudget(input.tokenBudget);
return await this.#withAccounting(async () => {
const existing = this.#host.getState();
if (existing?.goal && existing.goal.status !== "dropped" && existing.goal.status !== "complete") {
throw new Error("cannot create a new goal because this session already has a goal");
}
const state = this.#createGoalState(objective, input.tokenBudget);
this.#budgetReportedFor = undefined;
this.#markActiveAccounting(state.goal);
await this.#commitState(state, { persist: "goal" });
return state;
});
}
async replaceGoal(input: { objective: string; tokenBudget?: number }): Promise<GoalModeState> {
const objective = input.objective.trim();
if (!objective) throw new Error("objective is required when op=replace");
validateTokenBudget(input.tokenBudget);
return await this.#withAccounting(async () => {
const existing = this.#host.getState();
if (!existing?.enabled || !isAccountingStatus(existing.goal)) {
throw new Error("cannot replace goal because no goal is active");
}View on GitHub (pinned to 9690622007)
Solutions
- Call the drop operation on the current goal before creating a new one
- Use replaceGoal instead of createGoal to swap in a new objective
- Complete the existing goal first if it was actually achieved
- Check host.getState().goal.status before attempting creation
Example fix
// before
await runtime.createGoal({ objective: newObjective }); // throws if goal exists
// after
const state = runtime.getState?.() ?? host.getState();
if (state?.goal && state.goal.status !== 'dropped' && state.goal.status !== 'complete') {
await runtime.replaceGoal({ objective: newObjective });
} else {
await runtime.createGoal({ objective: newObjective });
} Defensive patterns
Strategy: type-guard
Validate before calling
const state = host.getState(); const canCreate = !state?.goal || state.goal.status === 'dropped' || state.goal.status === 'complete'; if (!canCreate) await dropOrReplaceFirst();
Type guard
function canCreateGoal(state: { goal?: { status: string } } | undefined | null): boolean {
return !state?.goal || state.goal.status === 'dropped' || state.goal.status === 'complete';
} Try / catch
try {
await runtime.createGoal({ objective });
} catch (err) {
if (err instanceof Error && err.message.includes('already has a goal')) {
await runtime.replaceGoal({ objective });
} else throw err;
} Prevention
- Read host.getState() before any goal mutation
- Track goal lifecycle in your own UI state to disable 'create' while one is live
- Prefer replaceGoal for 'new objective on existing goal' flows
When it happens
Trigger: Calling createGoal twice without dropping or completing the first goal; a persisted goal from a resumed session is still active; calling create when the intended op was replaceGoal.
Common situations: Scripts that retry goal creation after an unrelated error; users typing a new goal while the previous one is still tracked; forgetting to call the drop op before starting fresh.
Related errors
- cannot replace goal because no goal is active
- cannot complete a dropped goal
- No paused goal.
- Goal is already complete.
- cannot complete goal because no goal is active
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/d5f4d1201771a5fb.
Report an issue: GitHub.