can1357/oh-my-pi · error
No messages to continue from
Error message
No messages to continue from
What it means
Agent.continue() throws this when the transcript is empty (messages.length === 0) AND there are no queued steering or follow-up messages to deliver as the opening turn (packages/agent/src/agent.ts:1250). continue() resumes an existing conversation; with zero messages and an empty queue there is nothing to resume. The queued-message checks exist specifically so idle-drain callers don't spin on an undeliverable queue (issue #6344).
Source
Thrown at packages/agent/src/agent.ts:1250
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-drain
// callers (AgentSession#scheduleQueuedMessageDrain) re-arm continue() on every
// microtask because hasQueuedMessages() never clears, spinning an unbounded
// allocation loop until OOM (issue #6344).
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("No messages to continue from");
}
if (messages[messages.length - 1].role === "assistant") {
// A tail with unpaired runnable tool calls resumes by re-executing
// them (see `unpairedToolCallTail` in agent-loop). This must win over
// queued-message delivery: injecting a message between the tool_use
// 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);View on GitHub (pinned to 9690622007)
Solutions
- Check that the transcript is non-empty before calling continue(): if messages.length === 0 and nothing is queued, call prompt() with a new user message instead.
- If you intended to start a conversation, use prompt() rather than continue().
- If resuming from persisted state, verify the session file actually loaded messages (guard against a missing/empty session).
- If you meant to inject work, queue it via steer()/followUp() first so continue() can deliver it.
Example fix
// before
await agent.continue(); // throws when transcript is empty
// after
if (hasQueuedMessages(agent) || lastMessageRole(agent) !== undefined) {
await agent.continue();
} else {
await agent.prompt("resume work on task X");
} Defensive patterns
Strategy: validation
Validate before calling
function canContinue(agent: Agent): boolean {
const msgs = getMessages(agent);
return msgs.length > 0 || hasQueuedMessages(agent);
}
if (canContinue(agent)) {
await agent.continue();
} else {
await agent.prompt(initialUserMessage);
} Try / catch
try {
await agent.continue();
} catch (err) {
if (err instanceof Error && err.message === "No messages to continue from") {
await agent.prompt("Starting new task: ...");
return;
}
throw err;
} Prevention
- Only call continue() to resume an existing/interrupted conversation; use prompt() to start one.
- After reset() or clearMessages(), treat the agent as fresh and require a prompt().
- When restoring sessions, verify the transcript loaded non-empty before resuming.
- In idle-drain loops, stop when the transcript is empty and hasQueuedMessages() is false.
When it happens
Trigger: Calling agent.continue() on a freshly constructed or reset() Agent (no messages) with no pending steering/follow-up entries; calling continue() after clearMessages() on an empty session.
Common situations: Resume/retry logic that calls continue() unconditionally at startup before any prompt was ever made; a crash-recovery path that finds an empty transcript file; calling continue() after reset() drained both history and queues.
Related errors
- Cannot continue from message role: assistant
- 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/ffd7caca6534fa29.
Report an issue: GitHub.