a2aproject / a2aproject/a2a-java

[Bug]: SSE client rejects any event whose data arrives on a single line over 64KB, with no way to configure the limit

Open
#1,123 1 comment 1 reaction 1 assignee Claimed by @ehsavoie View on GitHub
Dominant language
Java
Stars
490
Forks
172
Avg merge
1d 6h
Merged PRs (30d)
55

Description

### What happened?

`ServerSentEventParser` enforces a hard-coded 64 KB limit on a single SSE line. Any A2A server that
serializes an event as one `data:` field larger than that is unreadable by the Java client, and there
is no way for an application to raise, lower or disable the limit.

We hit this against a production agent whose final `TaskArtifactUpdateEvent` carries a large result
artifact. The stream opens, runs, and then dies mid-flight:

java.lang.IllegalArgumentException: Line exceeds maximum length of 65536 characters
at org.a2aproject.sdk.client.http.ServerSentEventParser.processLine(ServerSentEventParser.java:58)

**Why we think this is a bug rather than intended hardening**

1. Neither the [SSE specification]
nor the A2A specification places any limit on line length, so a server emitting a 200 KB `data:`
line is fully conformant. The Java client is the only participant that rejects it.

2. The limit is inconsistent with the parser's own per-event budget. `MAX_BUFFER_CHARS` allows 1 MB
of data spread across up to `MAX_BUFFER_SIZE` = 1000 lines. So the *same* 200 KB payload is
accepted when the server splits it across several `data:` lines and rejected when it sends one.
The DoS exposure is identical either way — the parser buffers the same total — so the per-line cap
costs interoperability without buying protection the per-event caps do not already provide.

3. It is not interoperable with the other A2A SDKs. The Python SDK reads streams with `httpx-sse`,
which accumulates `data:` lines until a blank line and imposes no per-line ceiling. A server that
every Python client reads happily is unreadable from Java. In our case the producer is
`a2a-sdk==1.1.0` (Python) serving through `sse-starlette`, which emits `json.dumps(...)` as a
single `data:` field because compact JSON contains no newlines — so this is the default framing of
the reference Python server, not an unusual choice.

**What we expected**

Either of:

- the per-line limit to be configurable (constructor parameter, builder option, or system property),
alongside the existing buffer limits; or
- the per-line limit to be removed, leaving `MAX_BUFFER_CHARS` / `MAX_BUFFER_SIZE` as the DoS bound,
since they already cap total memory per event.

Making all three configurable together would be ideal — 1 MB per event is also reachable for agents
returning large artifacts.

**Minimal reproducer**

String hugeJson = "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":\"" + "x".repeat(70_000) + "\"}";

List events = new ArrayList<>();
List errors = new ArrayList<>();
ServerSentEventParser parser = new ServerSentEventParser(events::add, errors::add);

parser.processLine("data: " + hugeJson);
parser.processLine("");

// events is empty; errors holds IllegalArgumentException("Line exceeds maximum length of 65536 characters")
// Splitting the same payload across two `data:` lines parses fine.

**Environment**

- `a2a-java-sdk-client` / `a2a-java-sdk-http-client` `1.1.0.Final`, JDK transport
(`JdkA2AHttpClient`), JSON-RPC transport binding
- Also present unchanged in `v1.3.0.Final` and on `main` at time of writing
- Server: Python `a2a-sdk` 1.1.0 behind `sse-starlette` 3.4.6

**Source**

`http-client/src/main/java/org/a2aproject/sdk/client/http/ServerSentEventParser.java`

private static final int MAX_BUFFER_SIZE = 1000;
private static final int MAX_BUFFER_CHARS = 1024 * 1024; // 1 MB
private static final int MAX_LINE_LENGTH = 65536; // 64 KB

public void processLine(@Nullable String line) {
...
if (line.length() > MAX_LINE_LENGTH) {
handleError(new IllegalArgumentException("Line exceeds maximum length of " + MAX_LINE_LENGTH + " characters"));
skippingCurrentEvent = true;

Because `ServerSentEventParser` is shared across the HTTP client implementations, this affects every
transport that streams over SSE, not just the JDK one.

### Relevant log output

```shell
2026-09-01 11:30:35 WARN Downstream A2A call failed: method=SendStreamingMessage, rpcId=g-bdf28358-f7e1-41c1-91e1-417005473594,
url=http://localhost...:8000,
message=Line exceeds maximum length of 65536 characters
java.lang.IllegalArgumentException: Line exceeds maximum length of 65536 characters
at org.a2aproject.sdk.client.http.ServerSentEventParser.processLine(ServerSentEventParser.java:58)
```

### Code of Conduct

- [x] I agree to follow this project's Code of Conduct

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.