earendil-works/pi · error · Error

No messages to continue from

Error message

No messages to continue from

What it means

Agent.continue() means 'generate the next assistant turn from the existing transcript', so it reads the last message of agent.state.messages and throws when there is none. A freshly constructed Agent (or one whose initialState.messages is empty) has nothing to resume, and the error fires before the loop is entered. Seed the transcript with prompt() first.

Source

Thrown at packages/agent/src/agent.ts:368

	async prompt(input: string | AgentMessage | AgentMessage[], images?: ImageContent[]): Promise<void> {
		if (this.activeRun) {
			throw new Error(
				"Agent is already processing a prompt. Use steer() or followUp() to queue messages, or wait for completion.",
			);
		}
		const messages = this.normalizePromptInput(input, images);
		await this.runPromptMessages(messages);
	}

	/** Continue from the current transcript. The last message must be a user or tool-result message. */
	async continue(): Promise<void> {
		if (this.activeRun) {
			throw new Error("Agent is already processing. Wait for completion before continuing.");
		}

		const lastMessage = this._state.messages[this._state.messages.length - 1];
		if (!lastMessage) {
			throw new Error("No messages to continue from");
		}

		if (lastMessage.role === "assistant") {
			const queuedSteering = this.steeringQueue.drain();
			if (queuedSteering.length > 0) {
				await this.runPromptMessages(queuedSteering, { skipInitialSteeringPoll: true });
				return;
			}

			const queuedFollowUps = this.followUpQueue.drain();
			if (queuedFollowUps.length > 0) {
				await this.runPromptMessages(queuedFollowUps);
				return;
			}

			throw new Error("Cannot continue from message role: assistant");
		}

View on GitHub (pinned to 4af9d21d3b)

Solutions

  1. Call agent.prompt(...) first to seed the transcript; only continue once at least one message exists.
  2. When restoring from persistence, verify agent.state.messages.length > 0 before enabling continue.
  3. Guard retry logic so it only continues when a previous turn actually left messages behind.

Example fix

// before
const agent = new Agent({ streamFn });
await agent.continue(); // throws: no messages

// after
await agent.prompt("hello");
await agent.continue();
Defensive patterns

Strategy: validation

Validate before calling

if (agent.state.messages.length === 0) {
  await agent.prompt(seedMessage);
} else {
  await agent.continue();
}

Type guard

function hasTranscript(agent: Agent): boolean {
  return agent.state.messages.length > 0;
}

Try / catch

try {
  await agent.continue();
} catch (err) {
  if (err instanceof Error && err.message === "No messages to continue from") {
    await agent.prompt("let's start");
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: Calling continue() on a new Agent before any prompt(); constructing Agent with initialState whose messages array is empty or failed to load; continuing after an aborted first prompt where no message was committed.

Common situations: Auto-continue/retry logic running after a startup failure; a 'continue' control enabled before the first user message; persistence loaders that silently return an empty array.

Related errors


AI-assisted analysis of earendil-works/pi@4af9d21d3b (2026-08-24). Data as JSON: /api/errors/cadf1c89b7e1fb5c. Report an issue: GitHub.