can1357/oh-my-pi · error

Cannot continue: no messages in context

Error message

Cannot continue: no messages in context

What it means

agentLoopContinue continues an existing conversation by re-entering the agent loop with the context's message history. If context.messages is empty there is nothing to continue from, so it throws immediately (synchronously from the EventStream factory). This is a programmer/usage error: continue was called on a fresh or reset context instead of after at least one message.

Source

Thrown at packages/agent/src/agent-loop.ts:595

/**
 * Continue an agent loop from the current context without adding a new message.
 * Used for retries - context already has user message or tool results.
 *
 * **Important:** The last message in context must convert to a `user` or `toolResult` message
 * via `convertToLlm` — except for an assistant tail with unpaired runnable
 * tool calls (see {@link unpairedToolCallTail}), which resumes by executing
 * those calls first. Any other assistant tail is rejected here; other invalid
 * tails cannot be validated since `convertToLlm` is only called once per turn.
 */
export function agentLoopContinue(
	context: AgentContext,
	config: AgentLoopConfig,
	signal?: AbortSignal,
	streamFn?: StreamFn,
): EventStream<AgentEvent, AgentMessage[]> {
	if (context.messages.length === 0) {
		throw new Error("Cannot continue: no messages in context");
	}

	if (context.messages[context.messages.length - 1].role === "assistant" && !unpairedToolCallTail(context.messages)) {
		throw new Error("Cannot continue from message role: assistant");
	}

	const stream = createAgentStream();

	(async () => {
		const newMessages: AgentMessage[] = [];
		const currentContext: AgentContext = { ...context, messages: [...context.messages] };

		stream.push({ type: "agent_start" });

		try {
			await runLoop(currentContext, newMessages, config, signal, stream, streamFn);
		} catch (err) {
			stream.fail(err);

View on GitHub (pinned to 9690622007)

Solutions

  1. Use the initial-prompt entrypoint (agentLoop / prompt) instead of continue when the context has no messages.
  2. Restore the message history before continuing (reload session state into context.messages).
  3. Guard the call site: only invoke continue when context.messages.length > 0.

Example fix

// before
await agentLoopContinue(context, config);
// after
if (context.messages.length === 0) {
  await agentLoop(context, [userMessage], config);
} else {
  await agentLoopContinue(context, config);
}
Defensive patterns

Strategy: validation

Validate before calling

if (context.messages.length === 0) {
  return agentLoop(context, [initialUserMessage], config);
}
return agentLoopContinue(context, config);

Type guard

function canContinue(context: AgentContext): boolean {
  return context.messages.length > 0;
}

Try / catch

try {
  return agentLoopContinue(context, config);
} catch (err) {
  if (err instanceof Error && err.message.includes("no messages in context")) {
    return agentLoop(context, [initialUserMessage], config);
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling agentLoopContinue(context, config) (via stream) with a brand-new AgentContext, a context that was cleared, or before any user message was pushed.

Common situations: Resuming a session file that failed to load (messages never restored); calling continue on a newly constructed agent instead of prompt/first turn; error-recovery paths that retry continue after the context was rebuilt empty.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/058b8a4ba78bb996. Report an issue: GitHub.