Repository navigation
fix(client): normalize malformed agent card parsing errors - #1307
Conversation
🧪 Code Coverage (vs
|
There was a problem hiding this comment.
Checking isinstance(agent_card_data, dict) catches non-object root responses, but the _handle_*_compatibility helpers below still raise uncaught AttributeError/TypeError when nested fields have wrong types:
{"supportsAuthenticatedExtendedCard": true, "capabilities": null}(TypeErroron'extendedAgentCard' not in capabilities){"supportsAuthenticatedExtendedCard": true, "capabilities": []}(TypeErroron item assignment){"url": "https://example.com", "additionalInterfaces": [null]}(AttributeErroroniface.get){"skills": null}or{"skills": [null]}(TypeError/AttributeError){"security": "invalid"}(AttributeErroronsec_dict.items())
Could we catch (TypeError, AttributeError) in parse_agent_card and raise ParseError(...) from e (or explicitly raise ParseError for invalid nested types in the compatibility helpers) and add a couple of test cases for malformed nested fields?
Assisted-by: OpenAI GPT-6 <noreply@openai.com>
|
@mykytanetipa Thanks for the review. fdd70db wraps the |
🤖 I have created a release *beep* *boop* --- ## [1.2.3](v1.2.2...v1.2.3) (2026-10-17) ### Bug Fixes * **client:** ignore the patch version when preferring a 1.0 interface ([#1304](#1304)) ([cbb2d84](cbb2d84)) * **client:** normalize malformed agent card parsing errors ([#1307](#1307)) ([486f0f6](486f0f6)) * **compat:** block on a v0.3 gRPC and REST send without configuration ([#1334](#1334)) ([8160310](8160310)), closes [#1321](#1321) * **compat:** treat a v0.3 history_length of 0 as unlimited in JSON-RPC and REST ([#1312](#1312)) ([e86695c](e86695c)) * **jcs:** count open containers for the nesting bound, so an empty container leaf is refused ([#1294](#1294)) ([5653daf](5653daf)) * **server:** isolate push notification failures from the task lifecycle ([#1329](#1329)) ([0ee47d7](0ee47d7)) * **server:** mark internal lifecycle events done so a cancelled stream frees its producer ([#1336](#1336)) ([a4c4fc9](a4c4fc9)) * **server:** reject push URLs in shared address space ([#1326](#1326)) ([01da00b](01da00b)) * **server:** release the ActiveTask after a direct Message response ([#1297](#1297)) ([49ebce6](49ebce6)) * **server:** run follow-up messages in the sender's contextvars ([#1317](#1317)) ([a016002](a016002)) * **server:** warn when an AgentCard is missing required fields ([#1279](#1279)) ([4f4101c](4f4101c)) * **telemetry:** let trace_function alone decide a span's status ([#1309](#1309)) ([59dcbfb](59dcbfb)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
Description
When an agent-card endpoint returns HTTP 200 with valid JSON such as
null, an array, a string, a number, or a boolean, the compatibility helpers raiseAttributeErrororTypeError. Malformed nested legacy fields can raise the same exceptions even when the root is an object. These exceptions escapeA2ACardResolver.get_agent_card()instead of the documentedAgentCardResolutionError.Validate that the decoded value is a JSON object at the parser boundary. Convert
TypeErrorandAttributeErrorfrom the three compatibility helpers intoParseError, retaining their original cause. The resolver already wrapsParseErrorintoAgentCardResolutionError; protobuf parsing and signature verification retain their existing exception handling.Regression tests use
httpx.MockTransportwith real JSON decoding: eight non-object roots and all six malformed nested cases from the review (null/list capabilities, a null additional interface, null skills/a null skill, and string security). Every new case failed before its corresponding fix. The nested-field tests also verify the full exception cause chain.Validation
scripts/lint.sh(uv run --locked ruff check --fix,ruff format, andty check) passed.tyexits successfully with three existing unused-ignore warnings.uv run --locked pytest tests/client/test_card_resolver.py -q: 46 passed.uv run --locked pytest --ignore=tests/integration --cov=src --cov-report=term-missing -q: 1,788 passed, 169 skipped, 3 xfailed; 91% coverage.card_resolver.pyhas 100% statement and branch coverage.PYTHONIOENCODING=utf-8, sample integration smoke tests: 3 passed.os.killpg) and push-notification fixtures (a FastAPI local callable cannot be pickled under Windows multiprocessing). These platform limitations are outside this change.AI assistance: OpenAI Codex (GPT-6) was used to implement, reproduce, and test this change under the account owner's explicit authorization. The follow-up implements the human maintainer's review request and received another independent review before submission.