[Bug]: REST transport never signals normal stream completion to the client (SSE hangs until timeout)
メンテナーはふだん 1 日以内に返信
評価
調査の方向性
Start with RestTransport.sendMessageStreaming and the A2AHttpClient postAsyncSSE/getAsyncSSE completion callbacks, then compare the JSON-RPC listener behavior described in the issue. Trace how ClientTransport exposes stream outcomes and how the returned CompletableFuture could support cancellation. Done means REST streams report exactly one normal completion or error and in-flight streams can be released.
索引モデルが issue の本文から書いたものです。
説明
What happened?
What happened?
On the REST (HTTP+JSON) client transport, a streaming call (sendMessage / sendMessageStreaming, resubscribe) never delivers a normal-completion signal to the caller. When the remote finishes a stream cleanly, the client's Flow.Publisher / SSE consumer stays open until it times out, even though the upstream connection has already ended.
The JSON-RPC transport does not surface this the same way: its listener turns the connection-end into a terminal errorHandler(null) completion signal. RestTransport does not do the equivalent, so a REST client has no way to observe that a stream completed normally.
Root cause (as far as we traced it)
The HTTP layer already carries a completion callback — A2AHttpClient's postAsyncSSE / getAsyncSSE accept a completeRunnable. But:
RestTransport.sendMessageStreamingpasses a no-op for thatcompleteRunnableand discards the returnedCompletableFuture, so the transport neither forwards the "stream ended" signal nor retains a handle to the stream.close()on the REST transport is a no-op, so a client cannot release the upstream connection that way either.
The completion signal therefore exists at the HTTP layer and is dropped at the transport layer, never reaching ClientTransport / the client. This is the same no-op REST completion callback that #1170 / #1173 explicitly scoped out when fixing the duplicate-terminal-callback contract for the JSON-RPC listeners: #1170 says to "evaluate REST separately: its native and 0.3 implementations currently both use a no-op completion callback, so changing that is an API decision rather than a compat-only fix," and #1173 left REST as is on those grounds. This issue is to track that deferred REST decision.
Note the signal that's missing is "the connection ended," not "a terminal event arrived." Terminal-event detection alone is insufficient, because a stream can legitimately end with no final event — e.g. an interrupted state (input-required / auth-required), which per #975 / #756 is deliberately non-terminal and keeps the stream open. Those streams close only on connection-end.
Expected behavior
A REST streaming client should be able to observe exactly one terminal outcome per stream — a normal completion or an error — the way the JSON-RPC path does after #1173, and should be able to cancel/release an in-flight stream.
Actual behavior
Normal completion is silently dropped. A stream that ends without a terminal event (or whose terminal event the client isn't watching for) hangs until the caller's own SSE/emitter timeout fires.
Steps to reproduce
- Build a client against an agent whose card selects the REST (HTTP+JSON) transport.
- Start a streaming request (
sendMessagestreaming or resubscribe) against a remote that ends the stream by closing the connection without a finalTaskStatusUpdateEvent— e.g. a turn that ends oninput-required. - Observe that the client's stream never completes; it stays open until timeout.
Workaround
Decorate the A2AHttpClient for one stream's duration: wrap the completeRunnable handed to postAsyncSSE / getAsyncSSE so connection-end fires a caller-visible callback, and retain the returned CompletableFuture so the stream can be cancelled on caller disconnect. This recovers both capabilities the transport drops, but it reaches around the SDK for something the transport could expose directly.
Proposed fix
Thread the existing HTTP-layer completion callback through RestTransport and expose it on ClientTransport, mirroring what JSON-RPC effectively does after #1173. This is additive and backward-compatible — callers that don't supply a completion handler are unaffected. (@scarvel8 offered a similar PR in #310.)
Related
- #310 — feature request for a stream-completion callback (transport-agnostic); this issue is the REST-transport-specific bug behind it.
- #1170 / #1173 — fixed the JSON-RPC SSE terminal-callback contract and explicitly deferred REST as an API decision (#1170: "evaluate REST separately ... a no-op completion callback ... an API decision").
- #975 / #756 — interrupted states are intentionally non-terminal, which is why connection-end (not terminal-event detection) is the signal that must be exposed.
Relevant log output
Code of Conduct
- I agree to follow this project's Code of Conduct
- 主要言語
- Java
- スター
- 504
- フォーク
- 184
- 平均マージ
- 1日 13時間
- マージ済み PR(30日)
- 31
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
a2aproject/a2a-java のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
a2aproject/a2a-java#1012 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
a2aproject/a2a-java#464 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
Create a sample that demonstrates how to use a shared contextId across multiple tasks再び着手できるかも @tanish111 が 354 日前に担当しましたが、オープン中のプルリクエストはありません。 オープンsample
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
a2aproject/a2a-java#375 · コメント 5 件 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
-
難易度 3/5 1〜2日 初心者へのやさしさ 55/100
a2aproject/a2a-java#1207 ·
メンテナーはふだん 1 日以内に返信
-
Enable checkstyle failOnViolation=true対応中かも @ZYZ666-RGB が 2 日前に担当しました。 オープン
難易度 3/5 1〜2日 初心者へのやさしさ 58/100
a2aproject/a2a-java#1206 · コメント 2 件 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
a2aproject/a2a-java の issue をすべて見る
似ている issue
-
Clock.MakeDate continues execution and returns a rolled-over instant after dispatching error on invalid date対応中かも このイシューにリンクされたプルリクエストがオープン中、またはマージ済みです。 オープン
難易度 1/5 1時間未満 初心者へのやさしさ 82/100
mit-cml/appinventor-sources#4155 ·
メンテナーはふだん 1 日以内に返信
-
難易度 1/5 1〜3時間 初心者へのやさしさ 62/100
Hira-shi/PW1-DAI-Carrel-Egal-Eyer#28 ·
メンテナーはふだん 1 日以内に返信
-
`GET /v1/event/token/{uuid}` can report a BOM upload as done before policy evaluation and metrics have finished対応中かも @Zargath が今日担当しました。 オープンdefect in triage
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
DependencyTrack/dependency-track#7646 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
floci-io/floci#5425 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 85/100
objectionary/eo-graphs#80 ·