Skip to content

fix: DeleteTaskPushNotificationConfigAsync throws on valid null result - #433

Merged
darrelmiller merged 1 commit into
a2aproject:mainfrom
MertBasar0:fix/429-delete-push-config-null-result
Aug 18, 2026
Merged

fix: DeleteTaskPushNotificationConfigAsync throws on valid null result#433
darrelmiller merged 1 commit into
a2aproject:mainfrom
MertBasar0:fix/429-delete-push-config-null-result

Conversation

@MertBasar0

Copy link
Copy Markdown
Contributor

Summary

Fixes #429.

A2AClient.DeleteTaskPushNotificationConfigAsync always threw A2AException: "Failed to deserialize JSON-RPC result." against a spec-compliant server — including this SDK's own A2AJsonRpcProcessor, which answers delete with a success response whose result is null. A null result on success is valid JSON-RPC 2.0, and DeleteTaskPushNotificationConfig is the only A2A method with a void result, so it was the only method affected.

Root cause

The generic send helper unconditionally deserialized result and threw on null:

return rpcResponse.Result.Deserialize<TResult>(A2AJsonUtilities.DefaultOptions)
    ?? throw new A2AException("Failed to deserialize JSON-RPC result.", A2AErrorCode.InternalError);

Delete routed through it with TResult = object, leaving no path that tolerates a null result.

Change

  • Split the transport/error handling of SendJsonRpcRequestAsync<TResult> into a SendJsonRpcRequestCoreAsync helper that returns the raw JsonRpcResponse. The generic overload keeps its exact behavior (deserialize-or-throw) for all non-void methods.
  • Added a void-result SendJsonRpcRequestAsync overload that surfaces JSON-RPC errors but skips result deserialization, and routed DeleteTaskPushNotificationConfigAsync through it.

No server-side change: the processor's null-result response is already spec-compliant.

Tests

  • New DeletePushNotificationConfigAsync_CompletesOnNullResult reproduces the wire shape the server actually produces (Result = null); it fails with A2AException before this change and passes after. The pre-existing delete test mocked the result as {}, which is why the regression was invisible.
  • Full solution test run: all 4 test projects pass on net8.0 and net10.0 (825 tests, 0 failures).

🤖 Generated with Claude Code

DeleteTaskPushNotificationConfig is the only A2A method with a void
result. The generic JSON-RPC send helper unconditionally deserialized
the response result and threw A2AException when it was null, so the
client rejected the spec-compliant null-result success response that
A2AJsonRpcProcessor itself produces.

Split the transport/error handling into SendJsonRpcRequestCoreAsync and
route delete through a void-result overload that skips result
deserialization. Fixes a2aproject#429.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@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 refactors the A2AClient to support JSON-RPC methods that do not return a result (such as DeleteTaskPushNotificationConfigAsync) by introducing a non-generic SendJsonRpcRequestAsync overload and extracting the core request logic into SendJsonRpcRequestCoreAsync. A unit test has also been added to verify that the client completes successfully when receiving a null result. The review feedback suggests optimizing the new SendJsonRpcRequestAsync method by removing the async and await keywords and returning the task directly to avoid unnecessary state machine overhead.

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/Client/A2AClient.cs
@chopmob-cloud

Copy link
Copy Markdown
Contributor

Verified this independently against current main. The root cause is exactly as described: DeleteTaskPushNotificationConfigAsync routes through SendJsonRpcRequestAsync<object>, whose rpcResponse.Result.Deserialize<TResult>(...) ?? throw cannot tolerate a null result, while the SDK's own server answers delete with JsonRpcResponse.CreateJsonRpcResponse(requestId, (object?)null) (A2AJsonRpcProcessor.cs), so the client cannot consume its own server's success response. A null result member on success is valid JSON-RPC 2.0, and delete is the only void-result A2A method, so the blast radius is exactly this one call.

The fix shape looks right: splitting out SendJsonRpcRequestCoreAsync and adding a void overload keeps the null-strictness for every result-bearing method while letting delete complete, and the regression test pins the exact server response shape that used to throw.

One housekeeping note: all build and test checks are green, the only failing check is the PR title validator, so a conventional prefix like fix: ... on the title should clear the last red X.

@MertBasar0 MertBasar0 changed the title Fix DeleteTaskPushNotificationConfigAsync throwing on valid null result fix: DeleteTaskPushNotificationConfigAsync throws on valid null result Jul 14, 2026
@MertBasar0

Copy link
Copy Markdown
Contributor Author

@darrelmiller gentle ping on this one. It's a small client-side fix for #429: all checks are green, and another user independently reproduced both the bug and the root cause in the thread.

While I'm here — is review bandwidth on a2a-dotnet currently constrained? The last merge here was #405 on June 9, while a2a-python and a2a-js have both shipped releases in the past week, and a few external PRs (#432, this one) are sitting in the queue. I'm not asking to jump the line; I'd genuinely like to help. I also have #23 (the A2A-Extensions activation runtime) scoped and ready to implement if that's useful.

@MertBasar0

Copy link
Copy Markdown
Contributor Author

@SergeyMenshykh since you're back in the repo today — would you mind taking a look at this one? It's a small client-side fix for #429 (the only A2A method with a void result; the client rejects the spec-compliant null result its own server returns). All checks are green, and another user independently reproduced the bug and root cause in the thread.

Happy to reshape the void-result path if you'd prefer a different approach.

@darrelmiller
darrelmiller added this pull request to the merge queue Aug 18, 2026
Merged via the queue into a2aproject:main with commit 809013b Aug 18, 2026
6 of 7 checks passed
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.

A2AClient.DeleteTaskPushNotificationConfigAsync always throws "Failed to deserialize JSON-RPC result" on a valid null result

3 participants