What happened?
What happened
JsonUtil.PartTypeAdapter.read picks the oneOf discriminator as the first content key
whose value is not JSON null. An empty string is not null, so when a producer emits
every content key of the flattened oneOf and leaves the unset ones as "", the first
empty placeholder wins and the populated key is discarded.
A DataPart sent as {"text": "", "data": {...}} decodes as an empty TextPart, and the
data payload is silently lost — no exception, no warning.
The comment directly above the scan already describes the intended behaviour:
// Find the oneOf discriminator, skipping null/empty values to tolerate formats
// where multiple content keys may be present with only one populated
// (e.g., proto serialization with alwaysPrintFieldsWithNoPresence).
Only null is skipped; "" is not. So the exact producer the comment names — proto JSON
with alwaysPrintFieldsWithNoPresence — is the one case it fails to tolerate.
Reproduction
String json = """
{"text": "", "data": {"turns": [{"role": "user", "text": "hi"}]}}
""";
Part<?> part = JsonUtil.OBJECT_MAPPER.fromJson(json, Part.class);
// expected: DataPart carrying the turns
// actual: TextPart("") — the data payload is gone
Observed against org.a2aproject.sdk:a2a-java-sdk-jsonrpc-common:
| payload |
decodes as |
{"text": "", "data": {…}} |
TextPart("") — data lost |
{"data": {…}, "text": ""} |
DataPart (correct) |
{"text": null, "data": {…}} |
DataPart (correct) |
{"url": "", "data": {…}} |
FilePart — data lost |
The result depends on JSON key order, which producers are not obliged to control.
Versions
Reproduces on 1.0.0.Final and on 1.2.0.Final and current HEAD.
Expected
The first content key with a populated value should win, so that a producer emitting
empty placeholders round-trips. A part whose only content key is a deliberately empty
string ({"text": ""}) must still decode as an empty TextPart.
Impact
An agent receiving such a message answers on input the caller believes it sent. There is
no error to detect, so the failure is only visible as degraded output.
Relevant log output
Code of Conduct
What happened?
What happened
JsonUtil.PartTypeAdapter.readpicks theoneOfdiscriminator as the first content keywhose value is not JSON
null. An empty string is notnull, so when a producer emitsevery content key of the flattened
oneOfand leaves the unset ones as"", the firstempty placeholder wins and the populated key is discarded.
A
DataPartsent as{"text": "", "data": {...}}decodes as an emptyTextPart, and thedatapayload is silently lost — no exception, no warning.The comment directly above the scan already describes the intended behaviour:
Only
nullis skipped;""is not. So the exact producer the comment names — proto JSONwith
alwaysPrintFieldsWithNoPresence— is the one case it fails to tolerate.Reproduction
Observed against
org.a2aproject.sdk:a2a-java-sdk-jsonrpc-common:{"text": "", "data": {…}}datalost{"data": {…}, "text": ""}{"text": null, "data": {…}}{"url": "", "data": {…}}datalostThe result depends on JSON key order, which producers are not obliged to control.
Versions
Reproduces on
1.0.0.Finaland on1.2.0.Finaland current HEAD.Expected
The first content key with a populated value should win, so that a producer emitting
empty placeholders round-trips. A part whose only content key is a deliberately empty
string (
{"text": ""}) must still decode as an emptyTextPart.Impact
An agent receiving such a message answers on input the caller believes it sent. There is
no error to detect, so the failure is only visible as degraded output.
Relevant log output
Code of Conduct