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

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions