can1357/oh-my-pi · error
Cannot continue from message role: assistant
Error message
Cannot continue from message role: assistant
What it means
Agent.continue() throws this when the last message in the transcript has role "assistant" but the tail contains no unpaired runnable tool calls and no steering/follow-up messages are queued (packages/agent/src/agent.ts:1274). A completed assistant turn is a natural stopping point: there is no pending tool result to feed back and no new user input, so the loop cannot decide what to generate next. Continuation is only valid from a user/tool tail or an assistant tail mid tool-execution.
Source
Thrown at packages/agent/src/agent.ts:1274
// blocks and their results would break the provider's pairing
// invariant. Queued messages drain inside the resumed loop instead.
if (unpairedToolCallTail(messages)) {
await this.#runLoop(undefined, undefined, signal, true);
return;
}
const queuedSteering = await this.#dequeueSteeringMessagesAfterHooks(dequeueSignal);
if (queuedSteering.length > 0) {
await this.#runLoop(queuedSteering, { skipInitialSteeringPoll: true }, signal, true);
return;
}
const queuedFollowUp = await this.#dequeueFollowUpMessagesAfterHooks(dequeueSignal);
if (queuedFollowUp.length > 0) {
await this.#runLoop(queuedFollowUp, undefined, signal, true);
return;
}
throw new Error("Cannot continue from message role: assistant");
}
await this.#runLoop(undefined, undefined, signal, true);
} finally {
resolve();
if (this.#abortController === continuationAbortController) {
this.#state.isStreaming = false;
this.#state.streamMessage = null;
this.#state.pendingToolCalls.clear();
this.#abortController = undefined;
if (this.#runningPrompt === promise) {
this.#runningPrompt = undefined;
this.#resolveRunningPrompt = undefined;
}
}
}
}
View on GitHub (pinned to 9690622007)
Solutions
- Send a new user message with prompt() or queue one with followUp() instead of calling continue() — a finished assistant turn needs new input.
- Check the last message role before continuing: only continue when the tail is a user/tool message or an assistant message with unpaired tool calls.
- If your drain loop calls continue() repeatedly, stop when the turn completed and nothing is queued (the error signals that state).
- If you expected tool calls to be pending, verify the run didn't already complete them before the interruption.
Example fix
// before
await agent.continue(); // throws when last message is a final assistant reply
// after
const msgs = getMessages(agent);
const last = msgs[msgs.length - 1];
if (last?.role === "assistant") {
agent.followUp("please proceed");
} else {
await agent.continue();
} Defensive patterns
Strategy: validation
Validate before calling
function continueIsMeaningful(agent: Agent): boolean {
const msgs = getMessages(agent);
const last = msgs[msgs.length - 1];
if (!last) return false;
if (last.role !== "assistant") return true;
return hasQueuedMessages(agent) || hasUnpairedToolCalls(msgs);
}
if (continueIsMeaningful(agent)) await agent.continue(); Try / catch
try {
await agent.continue();
} catch (err) {
if (err instanceof Error && err.message.startsWith("Cannot continue from message role")) {
agent.followUp("please proceed with the next step");
return;
}
throw err;
} Prevention
- Treat a completed assistant turn as an endpoint: send new input via prompt()/followUp(), not continue().
- In auto-continue schedulers, stop the loop once the tail is a final assistant message with nothing queued.
- Inspect the last message role before resuming persisted sessions.
- Distinguish 'resume interrupted tool work' (continue) from 'continue the conversation' (prompt/followUp) in your API usage.
When it happens
Trigger: Calling continue() right after a normal assistant reply finished (turn completed cleanly); resuming a session whose transcript ends with a final assistant answer; an idle-drain loop re-invoking continue() after the agent already answered with nothing queued.
Common situations: Auto-continue/retry schedulers that keep calling continue() after every completed turn; loading a saved session that ended on a complete assistant message and attempting to resume it; confusion between 'continue the conversation' (use prompt/followUp) and 'resume interrupted work' (continue).
Related errors
- No messages to continue from
- Commit agent did not provide changelog entries.
- directory stack is empty
- No active model on agent
- No model configured
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/4ed263fbf0e42af5.
Report an issue: GitHub.