Skip to content

fix: relax A2A.V0_3.AgentMessage kind/messageId requirements - #435

Open
chopmob-cloud wants to merge 5 commits into
a2aproject:mainfrom
chopmob-cloud:fix/v03-message-optional-kind-messageid
Open

fix: relax A2A.V0_3.AgentMessage kind/messageId requirements#435
chopmob-cloud wants to merge 5 commits into
a2aproject:mainfrom
chopmob-cloud:fix/v03-message-optional-kind-messageid

Conversation

@chopmob-cloud

@chopmob-cloud chopmob-cloud commented Jul 8, 2026

Copy link
Copy Markdown

Fixes #396.

Problem

Real-world v0.3 agents return status.message payloads that omit kind and
messageId, a discrepancy between the .proto and JSON Schema definitions of
v0.3. A2A.V0_3.AgentMessage marked both [JsonRequired], so the whole task
response failed to deserialize with JSON deserialization for type 'A2A.V0_3.AgentMessage' was missing required properties including: 'kind', 'messageId'., even though the outer "kind": "task" discriminator was handled
correctly.

Fix

  • Kind (declared on the shared A2AEvent base): removed [JsonRequired].
    Presence of the discriminator is already enforced independently by
    BaseKindDiscriminatorConverter.Read for polymorphic deserialization
    (through A2AEvent or A2AResponse). The attribute only mattered for the
    concretely-typed path, where the converter never runs, exactly the case here
    (AgentTaskStatus.Message is declared as concrete AgentMessage?, not the
    polymorphic base). Verified the existing A2AEvent_Deserialize_MissingKind_Throws
    and A2AEvent_Deserialize_UnknownKind_Throws_A2AException tests, which exercise
    the polymorphic path, are unaffected: they still throw via the converter's own
    presence check, independent of the attribute.
  • MessageId: removed [JsonRequired]. Falls back to a lazily generated id
    (per review, a backing field with a lazy getter, so the default is only
    generated if the property is read without having been set, not on every
    construction) instead of an empty string, so a message deserialized without
    one still has a usable, unique identifier.

Tests

Adds tests/A2A.V0_3.UnitTests/GitHubIssues/Issue396.cs, mirroring the existing
GitHubIssues/Issue160.cs convention:

  • Issue_396_Passes: the exact payload shape from the issue report deserializes successfully.
  • MessageId_WhenNotSet_IsLazyAndUniquePerInstance: two separately constructed messages get distinct generated ids; repeated reads on the same instance return the same cached value.
  • MessageId_WhenSetFromJson_IsPreservedExactly: an explicit messageId in the payload is never overwritten by the lazy default.

Testing performed

No local .NET SDK in my primary environment, so built and tested on a separate
machine against a fresh clone of main (commit 8fe65cfaa6):

  • Regression-proof: added the new test against the unmodified source first, confirmed it fails with the exact error quoted in the issue (missing required properties including: 'kind', 'messageId'). Applied the fix, confirmed it passes.
  • Full suite (dotnet build/dotnet test --framework net10.0, root A2A.slnx, matching the buildSamples CI job): all 4 test projects passed, 827/827 tests.
  • dotnet build ./src/A2ALibs.slnx --framework net8.0 (buildLibs net8.0 leg): succeeded, 0 warnings, 0 errors.

…ization

A2AEvent.Kind was marked [JsonRequired], but presence of the discriminator
is already enforced independently by BaseKindDiscriminatorConverter.Read
for polymorphic deserialization (through A2AEvent or A2AResponse). The
attribute only mattered for concretely-typed deserialization, where the
converter never runs, e.g. AgentTaskStatus.Message : AgentMessage. Some
v0.3 producers omit kind there, and deserialization failed even though
the property already has a correct default from the constructor.

Part of a2aproject#396.
Some v0.3 producers return status.message without messageId (a
discrepancy between the .proto and JSON Schema definitions of v0.3).
[JsonRequired] rejected the whole response as a result. Defaults to a
freshly generated id, matching messageId being a stable identifier
rather than an empty placeholder.

Fixes a2aproject#396.
…ageId)

Deserializes the exact payload shape from the issue report: a v0.3
task response whose status.message omits both kind and messageId.
Asserts both fields fall back to sensible defaults instead of failing
deserialization.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request removes the [JsonRequired] attribute from the Kind property in A2AEvent and the MessageId property in AgentMessage to prevent deserialization failures when these properties are omitted by some producers. Additionally, MessageId now defaults to a newly generated GUID, and a unit test has been added to verify the changes. The feedback suggests optimizing the MessageId property using lazy initialization to avoid unnecessary allocations when the property is provided in the JSON payload.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/A2A.V0_3/Models/AgentMessage.cs Outdated
Per review: generating the default in the auto-property initializer ran
on every AgentMessage construction, including deserialization where the
JSON already carries a messageId, discarding the generated id
immediately. Uses a backing field and a lazy getter instead, so the
default is only generated if the property is actually read without
having been set.
Adds MessageId_WhenNotSet_IsLazyAndUniquePerInstance (two separately
constructed messages get distinct generated ids, and repeated reads on
the same instance return the same cached value) and
MessageId_WhenSetFromJson_IsPreservedExactly (an explicit messageId in
the payload is not overwritten by the lazy default).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Relax v0.3 response deserialization status.message missing 'kind' and 'messageId'

1 participant