can1357/oh-my-pi · error · AgentBusyError
Agent is already processing. Use steer() or followUp() to qu
Error message
Agent is already processing. Use steer() or followUp() to queue messages, or wait for completion.
What it means
Agent.continue() throws AgentBusyError when this.#state.isStreaming is true, meaning a prompt/continuation loop is currently running (packages/agent/src/agent.ts:1216-1219). The Agent serializes runs: only one turn loop may be active at a time. The error message directs you to steer() or followUp() to inject messages into the running loop, or to await completion.
Source
Thrown at packages/agent/src/agent.ts:1218
if (this.#abortController) signals.push(this.#abortController.signal);
if (signal) signals.push(signal);
if (this.#deadline !== undefined) {
const delay = this.#deadline - Date.now();
if (delay <= 0) {
const controller = new AbortController();
controller.abort(new DOMException("Deadline exceeded", "TimeoutError"));
signals.push(controller.signal);
} else {
signals.push(AbortSignal.timeout(delay));
}
}
if (signals.length === 0) return undefined;
return signals.length === 1 ? signals[0] : AbortSignal.any(signals);
}
async continue(signal?: AbortSignal) {
if (this.#state.isStreaming) {
throw new AgentBusyError();
}
const { promise, resolve } = Promise.withResolvers<void>();
this.#runningPrompt = promise;
this.#resolveRunningPrompt = resolve;
const continuationAbortController = new AbortController();
this.#abortController = continuationAbortController;
this.#state.isStreaming = true;
this.#state.streamMessage = null;
this.#state.error = undefined;
try {
const dequeueSignal = this.#continuationDequeueSignal(signal);
const messages = this.#state.messages;
if (messages.length === 0) {
// An empty transcript has nothing to resume, but a queued steer/follow-up
// must still be delivered as the opening turn — mirroring the assistant-tail
// branch below. Throwing here leaves the message undeliverable, and idle-drainView on GitHub (pinned to 9690622007)
Solutions
- Queue the message instead: use steer() (mid-run steering) or followUp() (next turn) rather than starting a new run.
- Await agent.waitForIdle() (or the promise returned by the in-flight prompt) before calling continue().
- Track run state at the call site with a mutex/queue so only one caller drives the Agent at a time.
- If you believe the agent is stuck, call agent.abort() first, wait for idle, then continue().
Example fix
// before
await agent.continue(); // throws AgentBusyError if a run is active
// after
await agent.waitForIdle();
// or, without waiting:
agent.followUp("take another look"); // queued into the running loop Defensive patterns
Strategy: try-catch
Validate before calling
function isAgentIdle(agent: Agent): boolean {
return !isStreaming(agent);
}
if (isAgentIdle(agent)) await agent.continue(); Try / catch
try {
await agent.continue();
} catch (err) {
if (err instanceof AgentBusyError) {
agent.followUp(pendingMessage); // queue instead of racing
return;
}
throw err;
} Prevention
- Designate a single owner (queue/mutex) that drives prompt/continue for the Agent instance.
- Await waitForIdle() before any new run; never call continue() from streaming event callbacks.
- Use steer()/followUp() to inject messages into a live run instead of starting parallel runs.
- Debounce UI triggers (buttons, timers) that can fire continue() while a run is active.
When it happens
Trigger: Calling agent.continue() (or prompt()) while a previous prompt()/continue() run has not finished — isStreaming is still true. Common with concurrent callers: an event handler and a retry timer both invoking continue(), or calling continue() inside a streaming event callback.
Common situations: Firing continue() on a timer or on every streamed event without checking idle state; retry logic that re-invokes continue() after an error while the original loop is still tearing down; UI button double-clicks; multiple async tasks sharing one Agent instance.
Related errors
- native spelling thread stopped
- No active model on agent
- No model configured
- No messages to continue from
- Cannot continue from message role: assistant
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/d9dbb013cbc91608.
Report an issue: GitHub.