Skip to content

[Bug]: _clean_empty silently drops legitimate falsy values (0, False, 0.0) #692

@cchinchilla-dev

Description

@cchinchilla-dev

What happened?

_clean_empty in src/a2a/utils/helpers.py is documented to "recursively remove empty strings, lists and dicts". The base case correctly targets only '', [], {}, but the dict and list filters use truthiness (if v) which also drops 0, False, and 0.0:

return {k: v for k, v in cleaned_dict.items() if v}    # drops 0, False, 0.0
return [v for v in cleaned_list if v]                   # drops 0, False, 0.0
return d if d not in ['', [], {}] else None             # correct

This affects canonicalize_agent_card() (the only caller), which produces canonical JSON for signature verification (RFC 8785). An AgentCard with e.g. AgentCapabilities(streaming=False) would have the field stripped after surviving model_dump(exclude_none=True), losing the distinction between "explicitly disabled" and "unspecified".

Reproduction:

from a2a.utils.helpers import _clean_empty

assert _clean_empty({"retries": 0}) == {}        # expected: {"retries": 0}
assert _clean_empty({"enabled": False}) == {}     # expected: {"enabled": False}
assert _clean_empty([0, 1, 2]) == [1, 2]          # expected: [0, 1, 2]

Suggested fix — align filters with the base case:

return {k: v for k, v in cleaned_dict.items() if v is not None}
return [v for v in cleaned_list if v is not None]

If this behavior is intentional, I may be missing some context — in that case, clarifying the docstring and base case to better reflect the current filtering semantics could help avoid confusion.

If it’s considered a bug, I’d be glad to open a PR with the proposed change and add regression tests as needed.

Relevant log output

No output — values are dropped silently.

Code of Conduct

  • I agree to follow this project's Code of Conduct

Metadata

Metadata

Assignees

Labels

component: coreIssues related to base data models, auth, gRPC interfaces, observability, and fundamental utilities.status:awaiting response

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions