Skip to content

a2a-sdk 1.0.1: '_upb.FieldDescriptor' object has no attribute 'is_repeated' on every message/send #1050

@bokelley

Description

@bokelley

Affected version: a2a-sdk==1.0.1 with protobuf<6 (default ecosystem state at the time of release)
Status: Already fixed in 1.0.2 — filing for visibility, yank request, and to capture a clean repro.

Symptom

Every JSON-RPC message/send call to a server built on a2a-sdk==1.0.1 returns:

{
  "jsonrpc": "2.0",
  "id": "r1",
  "error": {
    "code": -32603,
    "message": "'google._upb._message.FieldDescriptor' object has no attribute 'is_repeated'",
    "data": null
  }
}

Discovery (tools/list, /.well-known/agent-card.json) is unaffected — only mutating calls hit the validator.

Root cause

a2a-sdk==1.0.1 ships a validate_proto_required_fields decorator on RequestHandler.on_message_send:

# a2a/server/request_handlers/request_handler.py:284 (1.0.1)
async def async_wrapper(...):
    if params is not None:
        validate_proto_required_fields(params)
    ...

# a2a/utils/proto_utils.py:211 (1.0.1)
def _check_required_field_violation(msg, field):
    if field.is_repeated:        # ← attribute doesn't exist on _upb backend pre-protobuf-6
        ...

With protobuf<6 and the default C++ accelerated backend (google._upb._message.FieldDescriptor), is_repeated is not an attribute. It exists only on the pure-Python descriptor.FieldDescriptor.

a2a-sdk==1.0.2 reverted to the canonical, backend-agnostic check:

# a2a/utils/proto_utils.py:211 (1.0.2)
if field.label == FieldDescriptor.LABEL_REPEATED:
    ...

Minimal repro (no server)

from google.protobuf.descriptor import FieldDescriptor
from a2a.types_pb2 import SendMessageRequest

# 1.0.2 path — works on both backends
print("label-based:", any(f.label == FieldDescriptor.LABEL_REPEATED
                          for f in SendMessageRequest.DESCRIPTOR.fields))

# 1.0.1 path — fails on the default C++ backend with protobuf<6
for f in SendMessageRequest.DESCRIPTOR.fields:
    print(f.name, f.is_repeated)   # AttributeError on _upb.FieldDescriptor

End-to-end:

pip install 'a2a-sdk[http-server]==1.0.1' 'protobuf<6'
# ... start any A2A server ...
curl -X POST http://localhost:8080/ \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"message/send","id":"r1","params":{"message":{"role":"user","messageId":"m","kind":"message","parts":[{"kind":"data","data":{"skill":"any_skill","parameters":{}}}]}}}'
# → {"error":{"message":"'google._upb._message.FieldDescriptor' object has no attribute 'is_repeated'"}}

Adopter context

We hit this through adcp-client-python==4.4.0, which transitively pinned a2a-sdk<1.0.2,>=1.0.1 and didn't pin protobuf. That combination is broken end-to-end. adcp==4.4.1 switched to a2a-sdk<1.1,>=1.0.2 (pulling the upstream fix); adcp==4.4.2 reverted to <1.0.2,>=1.0.1 and instead pinned protobuf<8,>=6, working around the bug at the protobuf layer where _upb.FieldDescriptor.is_repeated does exist.

We're standardizing on the adcp 4.4.2 path — protobuf>=6 aligns with where the ecosystem is going. The bug in 1.0.1 is still worth mitigating because the protobuf>=6 pin isn't always inherited by adopters using a2a-sdk directly.

Asks

  1. Consider yanking 1.0.1 from PyPI — it's silently broken on the C++ protobuf backend without an explicit protobuf>=6 pin. 1.0.2 is fine; the yank just steers resolvers off the trap.
  2. If yanking isn't desirable, add an explicit protobuf>=6 pin to 1.0.1's metadata (yank-and-replace) so the protobuf-side workaround is discoverable from pip install.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions