google-gemini/gemini-cli · error

LegacyAgentSession.send() cannot be called while a stream is

Error message

LegacyAgentSession.send() cannot be called while a stream is active.

What it means

Thrown by `LegacyAgentSession.send` when a second send arrives while `_activeStreamId` is still set — i.e. a stream is mid-flight. The legacy adapter has no correlation semantics for interleaved in-stream sends (updates, elicitation responses), so it rejects all concurrent sends defensively until the active stream completes and clears the flag.

Source

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

    this._subscribers.add(callback);
    return () => {
      this._subscribers.delete(callback);
    };
  }

  async send(payload: AgentSend): Promise<{ streamId: string }> {
    const message = 'message' in payload ? payload.message : undefined;
    if (!message) {
      throw new Error(
        'LegacyAgentSession.send() only supports message sends for the moment.',
      );
    }

    if (this._activeStreamId) {
      // TODO: Interactive may eventually allow selected in-stream sends such as
      // updates or elicitation responses. Keep rejecting all concurrent sends
      // here until we define those correlation semantics.
      throw new Error(
        'LegacyAgentSession.send() cannot be called while a stream is active.',
      );
    }

    this._beginNewStream();
    const streamId = this._translationState.streamId;
    const parts = contentPartsToGeminiParts(message.content);
    const userMessage = this._makeUserMessageEvent(
      message.content,
      message.displayContent,
      payload._meta,
    );

    this._emit([userMessage]);

    this._scheduleRunLoop(parts, message.displayContent);

    return { streamId };

View on GitHub (pinned to 5024443c72)

Solutions

  1. Await the full resolution of the active stream (consume until `agent_end`) before issuing the next `send`.
  2. Serialize sends through a queue so only one stream is active at a time.
  3. Disable/ignore the submit affordance in the UI while a stream is active.
  4. If you genuinely need concurrent or in-stream sends, switch to a non-legacy session implementation that defines those semantics.

Example fix

// before
legacySession.send(msg1); // not awaited
legacySession.send(msg2); // throws — stream active

// after
for await (const _ of legacySession.stream(msg1)) { /* drain */ }
await legacySession.send(msg2); // previous stream finished
Defensive patterns

Strategy: validation

Validate before calling

// serialize sends through a queue
let sendChain: Promise<unknown> = Promise.resolve();
function queuedSend(session: LegacyAgentSession, payload: AgentSend) {
  const next = sendChain.then(() => session.send(payload));
  sendChain = next.catch(() => {}); // never break the chain
  return next;
}

Try / catch

try {
  await legacySession.send(payload);
} catch (e) {
  if (e instanceof Error && e.message.includes('while a stream is active')) {
    // enqueue the send for after the active stream ends
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Two `send` calls overlap in time: the first triggered `_beginNewStream()` and set `_activeStreamId`, and a second `send` was issued before the first stream's `agent_end` resolved. Also occurs when a caller tries to push an update into a running stream.

Common situations: A UI 'submit' button without debounce allowing double-submits; an orchestration loop that fires follow-up messages without awaiting the prior stream; an event-driven pipeline that reacts to partial output by sending again; mixing the legacy session with code that assumed concurrent send support.

Related errors


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