earendil-works/pi · error · Error

Agent is already processing a prompt. Use steer() or…

Error message

Agent is already processing a prompt. Use steer() or followUp() to queue messages, or wait for completion.

What it means

Agent.prompt() starts a new run, and the Agent executes one run at a time (a single activeRun slot). Calling prompt() again while a run is in flight makes the async method reject with this error to make the race visible; the message names the two supported alternatives - steer() to inject a message into the current run, or followUp() to queue a message that runs after the agent would otherwise stop. Nothing is queued or lost when the error throws.

Solutions

  1. If the input belongs to the current conversation while the agent is mid-run, use agent.steer(message) or agent.followUp(message) instead of prompt().
  2. Otherwise serialize: await agent.waitForIdle() (or await the earlier prompt()) before the next prompt().
  3. Route all user input through one dispatcher that prompts when idle and steers/follows up when busy.

Example fix

// before
void agent.prompt("first");   // not awaited
await agent.prompt("second"); // rejects: already processing

// after
const first = agent.prompt("first");
agent.steer({ role: "user", content: [{ type: "text", text: "second" }], timestamp: Date.now() });
await first;
Defensive patterns

Strategy: validation

Validate before calling

if (agent.state.isStreaming) {
  agent.followUp(message); // or steer(message) to inject into the current run
} else {
  await agent.prompt(message);
}

Try / catch

try {
  await agent.prompt(text);
} catch (err) {
  if (err instanceof Error && err.message.includes("already processing a prompt")) {
    agent.steer({ role: "user", content: [{ type: "text", text }], timestamp: Date.now() });
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: A second prompt() call before the first run's agent_end listeners settle; issuing prompts from unawaited event handlers; awaiting a first prompt() that an event listener keeps active past stream close; concurrent test prompts on a shared Agent.

Common situations: Chat front-ends where every user message calls prompt() without awaiting idle; timers/background jobs enqueueing prompts; sequentially written code where one prompt() was accidentally fire-and-forget.

Related errors


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

Appendix: source

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

		if (this.activeRun) {
			throw new Error("Agent is already processing. Wait for completion before resetting.");
		}

		this._state.messages = [];
		this._state.isStreaming = false;
		this._state.streamingMessage = undefined;
		this._state.pendingToolCalls = new Set<string>();
		this._state.errorMessage = undefined;
		this.clearFollowUpQueue();
		this.clearSteeringQueue();
	}

	/** Start a new prompt from text, a single message, or a batch of messages. */
	async prompt(message: AgentMessage | AgentMessage[]): Promise<void>;
	async prompt(input: string, images?: ImageContent[]): Promise<void>;
	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");
		}

View on GitHub (pinned to 4af9d21d3b)