can1357/oh-my-pi · error · Error

No paused goal.

Error message

No paused goal.

What it means

resumeGoal re-enables a paused goal, but it throws when the cloned state contains no goal at all — there is nothing to resume. Only a paused (or otherwise non-complete) existing goal can be resumed.

Source

Thrown at packages/coding-agent/src/goals/runtime.ts:422

		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");
			}
			await this.#flushUsageLocked("suppressed");
			const state = this.#createGoalState(objective, input.tokenBudget);
			this.#budgetReportedFor = undefined;
			this.#markActiveAccounting(state.goal);
			await this.#commitState(state, { persist: "goal" });
			return state;
		});
	}

	async resumeGoal(): Promise<GoalModeState> {
		return await this.#withAccounting(async () => {
			const state = this.#getStateClone();
			if (!state?.goal) throw new Error("No paused goal.");
			if (state.goal.status === "complete") throw new Error("Goal is already complete.");
			state.enabled = true;
			state.mode = "active";
			state.reason = undefined;
			state.goal.status = "active";
			state.goal.updatedAt = this.#now();
			this.#budgetReportedFor = undefined;
			this.#markActiveAccounting(state.goal);
			await this.#commitState(state, { persist: "goal" });
			return state;
		});
	}

	async pauseGoal(): Promise<GoalModeState | undefined> {
		return await this.#withAccounting(async () => {
			await this.#flushUsageLocked("suppressed");
			const state = this.#getStateClone();
			if (!state?.goal) return undefined;

View on GitHub (pinned to 9690622007)

Solutions

  1. Create a goal first with createGoal before attempting to resume
  2. Check getState()?.goal exists before calling resumeGoal
  3. Only wire the resume action in the UI when a paused goal exists

Example fix

// before
await runtime.resumeGoal(); // throws if no goal
// after
const state = host.getState();
if (!state?.goal) {
  await runtime.createGoal({ objective });
} else {
  await runtime.resumeGoal();
}
Defensive patterns

Strategy: validation

Validate before calling

const state = host.getState();
if (!state?.goal) throw new Error('resumeGoal requires an existing goal');

Type guard

function hasGoal(state: { goal?: unknown } | undefined | null): boolean {
  return !!state?.goal;
}

Try / catch

try {
  await runtime.resumeGoal();
} catch (err) {
  if (err instanceof Error && err.message === 'No paused goal.') {
    await runtime.createGoal({ objective });
  } else throw err;
}

Prevention

When it happens

Trigger: Calling resumeGoal on a session where no goal was ever created; calling it after the goal state was cleared/dropped; resuming on a fresh or restarted session without a persisted goal.

Common situations: Users pressing 'resume' with no goal set; scripts that unconditionally resume after a pause op that never ran; lost state after a restart without persistence.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/1b696c6ea2623973. Report an issue: GitHub.