google-gemini/gemini-cli · error

Cannot resume from eventId ${options.eventId} before agent_s

Error message

Cannot resume from eventId ${options.eventId} before agent_start for stream ${trackedStreamId}

What it means

Thrown by the same resume-by-eventId path when the referenced event exists but predates the `agent_start` event for its stream and no `agent_start` has been recorded for that stream at all. The session uses `agent_start` as the replay anchor; without it the wrapper cannot distinguish 'agent activity may begin later' from 'this send was acknowledged with no agent activity' and would risk an infinite wait, so it refuses.

Source

Thrown at packages/core/src/agent/agent-session.ts:162

        } else if (
          firstAgentStartIndex !== -1 &&
          firstAgentStartIndex <= index
        ) {
          replayStartIndex = index + 1;
          agentActivityStarted = true;
        } else if (firstAgentStartIndex !== -1) {
          // A pre-agent_start cursor can be resumed once the corresponding
          // agent activity is already present in history. Because stream()
          // yields only agent_start -> agent_end, replay begins at agent_start
          // rather than at the original pre-start event.
          replayStartIndex = firstAgentStartIndex;
        } else {
          // Consumers can only resume by eventId once the corresponding stream
          // has entered the agent_start -> agent_end lifecycle in history.
          // Without a recorded agent_start, this wrapper cannot distinguish
          // "agent activity may start later" from "this send was acknowledged
          // without agent activity" without risking an infinite wait.
          throw new Error(
            `Cannot resume from eventId ${options.eventId} before agent_start for stream ${trackedStreamId}`,
          );
        }
      } else if (options.streamId) {
        const index = currentEvents.findIndex(
          (e) => e.type === 'agent_start' && e.streamId === options.streamId,
        );
        if (index !== -1) {
          replayStartIndex = index;
        }
      } else {
        const activeStarts = currentEvents.filter(
          (e) => e.type === 'agent_start',
        );
        for (let i = activeStarts.length - 1; i >= 0; i--) {
          const start = activeStarts[i];
          if (
            !currentEvents.some(

View on GitHub (pinned to 5024443c72)

Solutions

  1. Wait until the stream has emitted `agent_start` before attempting to resume by eventId.
  2. Resume by `streamId` against an already-running stream instead of by pre-start eventId.
  3. If the stream was abandoned, start a fresh send rather than resuming.
  4. Replay the event log and confirm an `agent_start` with the matching `streamId` is present before calling resume.

Example fix

// before — resuming from a pre-start event
await session.send({ eventId: ackEventId }); // throws

// after — wait for agent_start, then resume by streamId
const started = session.events.find(
  (e) => e.type === 'agent_start' && e.streamId === ackEventIdStreamId,
);
if (started) {
  await session.send({ streamId: ackEventIdStreamId });
}
Defensive patterns

Strategy: validation

Validate before calling

function assertStreamStarted(
  events: { type: string; streamId?: string }[],
  streamId?: string,
) {
  if (!streamId) return;
  const started = events.some(
    (e) => e.type === 'agent_start' && e.streamId === streamId,
  );
  if (!started) {
    throw new Error(`Stream ${streamId} has no agent_start; wait or start fresh.`);
  }
}

const streamId = events.find((e) => e.id === eventId)?.streamId;
assertStreamStarted(events, streamId);

Type guard

function hasAgentStarted(
  events: { type: string; streamId?: string }[],
  streamId: string,
): boolean {
  return events.some((e) => e.type === 'agent_start' && e.streamId === streamId);
}

Try / catch

try {
  await session.send({ eventId });
} catch (e) {
  if (e instanceof Error && e.message.includes('before agent_start')) {
    // wait for the stream to produce agent_start, then resume by streamId
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Resuming from an eventId whose streamId has no `agent_start` in the events array (the stream was acknowledged but never entered the agent lifecycle). This covers the case where `firstAgentStartIndex === -1` and the resume event is not an `agent_end`.

Common situations: Resuming immediately after a send that was queued but not yet picked up by an agent; resuming from an event emitted during a failed/cancelled stream that never produced `agent_start`; race where the consumer persisted an id before the lifecycle event landed.

Related errors


AI-assisted analysis of google-gemini/gemini-cli@5024443c72 (2026-08-12). Data as JSON: /api/errors/3c9c84da772883bc. Report an issue: GitHub.