{"record":{"id":"298b906d3676af58","repo":"google-gemini/gemini-cli","slug":"legacyagentsession-send-cannot-be-called-while-a","errorCode":null,"errorMessage":"LegacyAgentSession.send() cannot be called while a stream is active.","messagePattern":"LegacyAgentSession\\.send\\(\\) cannot be called while a stream is active\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/core/src/agent/legacy-agent-session.ts","lineNumber":120,"sourceCode":"    this._subscribers.add(callback);\n    return () => {\n      this._subscribers.delete(callback);\n    };\n  }\n\n  async send(payload: AgentSend): Promise<{ streamId: string }> {\n    const message = 'message' in payload ? payload.message : undefined;\n    if (!message) {\n      throw new Error(\n        'LegacyAgentSession.send() only supports message sends for the moment.',\n      );\n    }\n\n    if (this._activeStreamId) {\n      // TODO: Interactive may eventually allow selected in-stream sends such as\n      // updates or elicitation responses. Keep rejecting all concurrent sends\n      // here until we define those correlation semantics.\n      throw new Error(\n        'LegacyAgentSession.send() cannot be called while a stream is active.',\n      );\n    }\n\n    this._beginNewStream();\n    const streamId = this._translationState.streamId;\n    const parts = contentPartsToGeminiParts(message.content);\n    const userMessage = this._makeUserMessageEvent(\n      message.content,\n      message.displayContent,\n      payload._meta,\n    );\n\n    this._emit([userMessage]);\n\n    this._scheduleRunLoop(parts, message.displayContent);\n\n    return { streamId };","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/google-gemini/gemini-cli/blob/5024443c7217464a66e98f80d73172a26440bd8f/packages/core/src/agent/legacy-agent-session.ts#L102-L138","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Await the full resolution of the active stream (consume until `agent_end`) before issuing the next `send`.","Serialize sends through a queue so only one stream is active at a time.","Disable/ignore the submit affordance in the UI while a stream is active.","If you genuinely need concurrent or in-stream sends, switch to a non-legacy session implementation that defines those semantics."],"exampleFix":"// before\nlegacySession.send(msg1); // not awaited\nlegacySession.send(msg2); // throws — stream active\n\n// after\nfor await (const _ of legacySession.stream(msg1)) { /* drain */ }\nawait legacySession.send(msg2); // previous stream finished","handlingStrategy":"validation","validationCode":"// serialize sends through a queue\nlet sendChain: Promise<unknown> = Promise.resolve();\nfunction queuedSend(session: LegacyAgentSession, payload: AgentSend) {\n  const next = sendChain.then(() => session.send(payload));\n  sendChain = next.catch(() => {}); // never break the chain\n  return next;\n}","typeGuard":null,"tryCatchPattern":"try {\n  await legacySession.send(payload);\n} catch (e) {\n  if (e instanceof Error && e.message.includes('while a stream is active')) {\n    // enqueue the send for after the active stream ends\n    return;\n  }\n  throw e;\n}","preventionTips":["Serialize sends per session with a promise chain or queue.","In UIs, disable submit while a stream is active.","Drain streams to completion (until agent_end) before issuing the next send.","If concurrency is required, upgrade to a session that defines correlation semantics."],"tags":["agent-session","legacy","concurrency","lifecycle"],"backgroundTag":null,"analyzedSha":"5024443c7217464a66e98f80d73172a26440bd8f","analyzedAt":"2026-08-12T06:01:53.711Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}