{"record":{"id":"905f6b9d7e89c06c","repo":"can1357/oh-my-pi","slug":"goal-is-already-complete","errorCode":null,"errorMessage":"Goal is already complete.","messagePattern":"Goal is already complete\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/goals/runtime.ts","lineNumber":423,"sourceCode":"\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}\n\t\t\tawait this.#flushUsageLocked(\"suppressed\");\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 resumeGoal(): Promise<GoalModeState> {\n\t\treturn await this.#withAccounting(async () => {\n\t\t\tconst state = this.#getStateClone();\n\t\t\tif (!state?.goal) throw new Error(\"No paused goal.\");\n\t\t\tif (state.goal.status === \"complete\") throw new Error(\"Goal is already complete.\");\n\t\t\tstate.enabled = true;\n\t\t\tstate.mode = \"active\";\n\t\t\tstate.reason = undefined;\n\t\t\tstate.goal.status = \"active\";\n\t\t\tstate.goal.updatedAt = this.#now();\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 pauseGoal(): Promise<GoalModeState | undefined> {\n\t\treturn await this.#withAccounting(async () => {\n\t\t\tawait this.#flushUsageLocked(\"suppressed\");\n\t\t\tconst state = this.#getStateClone();\n\t\t\tif (!state?.goal) return undefined;\n\t\t\tstate.enabled = false;","sourceCodeStart":405,"sourceCodeEnd":441,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/goals/runtime.ts#L405-L441","documentation":"resumeGoal refuses to resume a goal whose status is 'complete'. A completed goal is finalized (mode disabled, reason 'completed'); resuming it would contradict the recorded outcome, so the runtime throws instead.","triggerScenarios":"Calling resumeGoal after completeGoalFromTool finished the goal; resuming a persisted session whose goal already reached 'complete'; retry logic that calls resume after the tool reported completion.","commonSituations":"UI offering resume on a finished goal; scripts looping over pause/resume without re-checking status; user wanting to keep working after completion (needs a new goal instead).","solutions":["Treat the goal as done; create a new goal via createGoal if more work is needed","Check state.goal.status !== 'complete' before resuming","Disable the resume action when status is 'complete'"],"exampleFix":"// before\nawait runtime.resumeGoal(); // throws when complete\n// after\nconst state = host.getState();\nif (state?.goal?.status === 'complete') {\n  await runtime.createGoal({ objective: nextObjective });\n} else {\n  await runtime.resumeGoal();\n}","handlingStrategy":"validation","validationCode":"const state = host.getState();\nif (state?.goal?.status === 'complete') throw new Error('goal already complete; create a new one');","typeGuard":"function isResumable(state: { goal?: { status: string } } | undefined | null): boolean {\n  return !!state?.goal && state.goal.status !== 'complete';\n}","tryCatchPattern":"try {\n  await runtime.resumeGoal();\n} catch (err) {\n  if (err instanceof Error && err.message === 'Goal is already complete.') {\n    return { ok: false, reason: 'already-complete' }; // not an error for the user\n  }\n  throw err;\n}","preventionTips":["Treat 'complete' as terminal in all goal UI flows","Make resume handlers idempotent: check status first","After completion, switch the UI to new-goal creation instead of resume"],"tags":["state","goals"],"backgroundTag":"invalid-state-transition","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}