Summary
When using the /run_sse endpoint in google/adk-python (v1.22.1), the SSE stream sometimes does not close after the model's final response. Other times, the connection closes properly.
Details
- Repository: google/adk-python
- Version: 1.22.1
- Model: Claude (claude-opus-4-5-20251101) via LiteLLM backend
- Endpoint:
/run_sse
- Client: Browser/Angular with fetch and streaming response parsing , I am using my own UI
Observed Behavior
- The SSE connection usually closes as expected after the final text-only model message (no function call parts).
- However, in some runs (see example 1 below), the server keeps the connection open even after sending the final message. The client waits for more data forever.
- There is no explicit
turnComplete or done:true signal. The client must rely on fragile heuristics which are not robust to all edge cases.
Example 1: Connection stays open
my sessions are usually 5 to 10mins long
(Final event)
{"content":{"parts":[{"text":"Fixed! Added the missing `required` array... "}]}, ... }
— Connection does not close after this (bug)
Example 2: Connection closes as expected
(Final event)
{"content":{"parts":[{"text":"Your portfolio is ready! ..."}]}, ... }
— Connection closes as expected
What we've tried
- Attempted to detect stream completion by checking message structure, but this workaround does not work in all cases as sometimes the stream does close properly, sometimes it doesn't.
- Adding client-side completion timeouts as a hacky workaround, but this is not ideal.
Expected Behavior
- The SSE connection should ALWAYS close after the model's final response, not randomly.
- (Ideally) The server should send a clear termination signal: either closing the HTTP stream or emitting an explicit
{ "done": true } or similar message before closing.
Impact
- Causes frontends to hang indefinitely (no timeout event) and makes completion detection fragile.
- Client cannot reliably distinguish between server inactivity, error, or true completion.
Possible Causes (hypotheses)
- Async generator (
runner.run_async()) not always completing cleanly
- Some internal ADK state or tool connections keep the generator alive on certain tool execution chains
- Lack of an explicit "done" signal makes client-side detection error-prone
Request
- Please investigate why, under some (but not all) flows, the stream stays open after the model finishes.
- Please either:
- Fix the
runner.run_async()/SSE handler to always close the HTTP response after the final turn, OR
- Send an explicit termination/"done" event at the end so clients can handle this robustly.
Additional Context
- If helpful, I can provide full sample logs/events for both the working and non-working cases.
Summary
When using the
/run_sseendpoint in google/adk-python (v1.22.1), the SSE stream sometimes does not close after the model's final response. Other times, the connection closes properly.Details
/run_sseObserved Behavior
turnCompleteordone:truesignal. The client must rely on fragile heuristics which are not robust to all edge cases.Example 1: Connection stays open
my sessions are usually 5 to 10mins long
(Final event)
{"content":{"parts":[{"text":"Fixed! Added the missing `required` array... "}]}, ... }— Connection does not close after this (bug)
Example 2: Connection closes as expected
(Final event)
{"content":{"parts":[{"text":"Your portfolio is ready! ..."}]}, ... }— Connection closes as expected
What we've tried
Expected Behavior
{ "done": true }or similar message before closing.Impact
Possible Causes (hypotheses)
runner.run_async()) not always completing cleanlyRequest
runner.run_async()/SSE handler to always close the HTTP response after the final turn, ORAdditional Context