{"record":{"id":"d5f4d1201771a5fb","repo":"can1357/oh-my-pi","slug":"cannot-create-a-new-goal-because-this-session-alre","errorCode":null,"errorMessage":"cannot create a new goal because this session already has a goal","messagePattern":"cannot create a new goal because this session already has a goal","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/goals/runtime.ts","lineNumber":391,"sourceCode":"\t\t\tobjective,\n\t\t\tstatus: \"active\",\n\t\t\ttokenBudget,\n\t\t\ttokensUsed: 0,\n\t\t\ttimeUsedSeconds: 0,\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t};\n\t\treturn { enabled: true, mode: \"active\", goal };\n\t}\n\n\tasync createGoal(input: { objective: string; tokenBudget?: number }): Promise<GoalModeState> {\n\t\tconst objective = input.objective.trim();\n\t\tif (!objective) throw new Error(\"objective is required when op=create\");\n\t\tvalidateTokenBudget(input.tokenBudget);\n\t\treturn await this.#withAccounting(async () => {\n\t\t\tconst existing = this.#host.getState();\n\t\t\tif (existing?.goal && existing.goal.status !== \"dropped\" && existing.goal.status !== \"complete\") {\n\t\t\t\tthrow new Error(\"cannot create a new goal because this session already has a goal\");\n\t\t\t}\n\t\t\tconst state = this.#createGoalState(objective, input.tokenBudget);\n\t\t\tthis.#budgetReportedFor = undefined;\n\t\t\tthis.#markActiveAccounting(state.goal);\n\t\t\tawait this.#commitState(state, { persist: \"goal\" });\n\t\t\treturn state;\n\t\t});\n\t}\n\n\tasync replaceGoal(input: { objective: string; tokenBudget?: number }): Promise<GoalModeState> {\n\t\tconst objective = input.objective.trim();\n\t\tif (!objective) throw new Error(\"objective is required when op=replace\");\n\t\tvalidateTokenBudget(input.tokenBudget);\n\t\treturn await this.#withAccounting(async () => {\n\t\t\tconst existing = this.#host.getState();\n\t\t\tif (!existing?.enabled || !isAccountingStatus(existing.goal)) {\n\t\t\t\tthrow new Error(\"cannot replace goal because no goal is active\");\n\t\t\t}","sourceCodeStart":373,"sourceCodeEnd":409,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/goals/runtime.ts#L373-L409","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nawait runtime.createGoal({ objective: newObjective }); // throws if goal exists\n// after\nconst state = runtime.getState?.() ?? host.getState();\nif (state?.goal && state.goal.status !== 'dropped' && state.goal.status !== 'complete') {\n  await runtime.replaceGoal({ objective: newObjective });\n} else {\n  await runtime.createGoal({ objective: newObjective });\n}","handlingStrategy":"type-guard","validationCode":"const state = host.getState();\nconst canCreate = !state?.goal || state.goal.status === 'dropped' || state.goal.status === 'complete';\nif (!canCreate) await dropOrReplaceFirst();","typeGuard":"function canCreateGoal(state: { goal?: { status: string } } | undefined | null): boolean {\n  return !state?.goal || state.goal.status === 'dropped' || state.goal.status === 'complete';\n}","tryCatchPattern":"try {\n  await runtime.createGoal({ objective });\n} catch (err) {\n  if (err instanceof Error && err.message.includes('already has a goal')) {\n    await runtime.replaceGoal({ objective });\n  } else throw err;\n}","preventionTips":["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"],"tags":["state","goals","conflict"],"backgroundTag":"resource-already-exists","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}