earendil-works/pi · error · Error
Agent is already processing. Wait for completion before cont
Error message
Agent is already processing. Wait for completion before continuing.
What it means
Agent.continue() resumes an idle agent from its transcript; like prompt() and reset() it is an idle-only operation and rejects while activeRun exists. The single-run design means a continuation cannot be started on top of a run that is still streaming or still settling its agent_end listeners. steer() and followUp() are the concurrency-safe ways to add input while a run is active.
Source
Thrown at packages/agent/src/agent.ts:363
}
/** 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");
}
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;View on GitHub (pinned to 4af9d21d3b)
Solutions
- await agent.waitForIdle() before calling continue().
- If you wanted to inject input mid-run, use steer()/followUp() - continue() is only for resuming an idle agent.
- Await each prompt()/continue() fully (including listener settlement) before scheduling the next.
Example fix
// before
void agent.prompt("hi");
await agent.continue(); // rejects: previous run still active
// after
await agent.prompt("hi");
await agent.waitForIdle();
await agent.continue(); Defensive patterns
Strategy: validation
Validate before calling
await agent.waitForIdle(); // no-op when idle await agent.continue();
Try / catch
try {
await agent.continue();
} catch (err) {
if (err instanceof Error && err.message.includes("before continuing")) {
await agent.waitForIdle();
await agent.continue(); // retry once the run settled
} else {
throw err;
}
} Prevention
- Never schedule continue() from a timer without checking idle first.
- Await each run fully, including agent_end listener settlement.
- Use steer()/followUp() for mid-run input instead of continue().
When it happens
Trigger: Calling agent.continue() while a prompt()/continue() run is in flight; auto-continue loops that fire on a timer without checking idle; calling continue() from an event listener that executes before the run's agent_end listeners settle.
Common situations: Orchestrators that continue after each turn in a while-loop without awaiting; retry schedulers; UI resume buttons racing an in-progress run.
Related errors
- Agent is already processing. Wait for completion before rese
- Agent is already processing a prompt. Use steer() or followU
- No messages to continue from
- Cannot continue from message role: assistant
AI-assisted analysis of earendil-works/pi@4af9d21d3b (2026-08-24).
Data as JSON: /api/errors/77cd0c1a23a272de.
Report an issue: GitHub.