Repository navigation
Description
Summary
The v1.0 Python SDK does not validate that REQUIRED proto fields are present when processing SendMessage JSON-RPC requests. A message without messageId, role, or parts is silently accepted and processed instead of returning InvalidParamsError (-32602).
Spec Reference
A2A v1.0 specification/a2a.proto — Message definition:
message Message {
string message_id = 1 [(google.api.field_behavior) = REQUIRED];
string context_id = 2;
string task_id = 3;
Role role = 4 [(google.api.field_behavior) = REQUIRED];
repeated Part parts = 5 [(google.api.field_behavior) = REQUIRED];
google.protobuf.Struct metadata = 6;
repeated string extensions = 7;
repeated string reference_task_ids = 8;
}All three of message_id, role, and parts are marked REQUIRED. A server receiving a message missing any of these MUST return an error per proto field_behavior semantics.
Reproduction
Against any v1.0 SDK agent:
# Missing messageId:
curl -X POST http://localhost:9997 -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"SendMessage","params":{"message":{"role":"ROLE_USER","parts":[{"text":"no id"}]}},"id":"1"}'
# ACTUAL: {"result": {"task": {...}}} ← SUCCESS (should be error)
# EXPECTED: {"error": {"code": -32602, "message": "Missing required field: messageId"}}
# Missing role:
curl -X POST http://localhost:9997 -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"SendMessage","params":{"message":{"messageId":"msg-1","parts":[{"text":"no role"}]}},"id":"1"}'
# ACTUAL: {"result": {"task": {...}}} ← SUCCESS (should be error)
# Missing parts:
curl -X POST http://localhost:9997 -H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"SendMessage","params":{"message":{"messageId":"msg-1","role":"ROLE_USER"}},"id":"1"}'
# ACTUAL: {"result": {"task": {...}}} ← SUCCESS (should be error)Root Cause
In proto3 JSON deserialization, missing fields are silently treated as the zero/default value:
- Missing
message_id→ empty string"" - Missing
role→ROLE_UNSPECIFIED (0) - Missing
parts→ empty list[]
The SDK (via google.protobuf.json_format.ParseDict) does not honor google.api.field_behavior = REQUIRED as a validation constraint during deserialization.
Affected code: a2a/server/apps/jsonrpc/jsonrpc_app.py — _decode_request() or the calling handler should validate required fields.
Expected Fix
Add a validation step after proto deserialization that checks for empty/zero-value REQUIRED fields:
def _validate_message(message: Message) -> None:
if not message.message_id:
raise InvalidParamsError(message='messageId is required')
if message.role == Role.ROLE_UNSPECIFIED:
raise InvalidParamsError(message='role is required')
if not message.parts:
raise InvalidParamsError(message='parts is required')Impact
- TCK test
test_missing_required_message_fields(optional/features) fails for all v1.0 SDK agents - Agents may process malformed messages and produce undefined behavior
- Protocol compliance gap: agents are supposed to reject invalid messages
Context
Discovered during A2A TCK Round 2 gap analysis. This is distinct from #872 (ListTasks pagination). The SDK version tested is a2a-sdk==1.0.0a0.
Activity
ishymko commentedon Mar 20, 2026
Related to #845
zeroasterisk commentedon Mar 20, 2026
Note on severity: Per AIP-203,
google.api.field_behavior = REQUIREDis descriptive — "The vocabulary given in this document is for descriptive purposes only, and does not itself add any validation." Proto3 deserialization will silently default missing strings to"", enums to0, and repeated fields to[].So this is more of a missing server-side validation than a "bug" in the traditional sense — the SDK correctly deserializes per proto3 rules, but doesn't enforce the API contract expressed by the annotations. The TCK does test for this (optional test
test_missing_required_message_fields), so agents built with the SDK will fail that check.Whether enforcement belongs in the SDK framework vs. individual agent handlers is a design question for the maintainers.
feat(server): validate presence according to `google.api.field_behavi…