Skip to content

fix: align REST message validation and SSE error format with JSON-RPC - #451

Open
ez-lbz wants to merge 2 commits into
a2aproject:mainfrom
ez-lbz:fix/rest-validation-sse-errors
Open

fix: align REST message validation and SSE error format with JSON-RPC#451
ez-lbz wants to merge 2 commits into
a2aproject:mainfrom
ez-lbz:fix/rest-validation-sse-errors

Conversation

@ez-lbz

@ez-lbz ez-lbz commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

What changed

1. REST message/send and message:stream validate empty message parts

Problem: The REST endpoints (/message:send, /message:stream) bind the request with [FromBody], which does not validate that Message.Parts is non-empty. The JSON-RPC endpoint validates this in DeserializeAndValidate and rejects empty parts with InvalidParams. A REST client could send a message with no parts, which the server accepted — a REST/JSON-RPC validation gap.

Fix (src/A2A.AspNetCore/A2AHttpProcessor.cs):

  • Added ValidateSendMessageRequest called at the start of SendMessageRestAsync and SendMessageStreamRest.
  • Empty Message.Parts now throws A2AException("Message parts cannot be empty", A2AErrorCode.InvalidParams), which the existing exception handling maps to HTTP 400 — identical behavior to the JSON-RPC binding.

2. REST SSE error events use the structured JSON-RPC error shape

Problem: On a mid-stream error, the REST SSE writer (A2AEventStreamResult) emitted a hardcoded data: {"error":"An internal error occurred during streaming."} — losing the A2A error code, the exception message, and the structured data details. The JSON-RPC SSE stream (JsonRpcStreamedResult) returns a full code/message/data error object. Clients switching between transports got inconsistent error handling.

Fix (src/A2A.AspNetCore/A2AHttpProcessor.cs):

  • A2AEventStreamResult now builds a structured error event: {"error":{"code":..,"message":..,"data":..}} via BuildErrorJson.
  • A2AException error codes are preserved (code = the A2A JSON-RPC error code, e.g. -32001); unexpected exceptions fall back to -32603 (InternalError) with a generic message so internal details are never leaked.
  • For A2A-specific error codes, data carries the same google.rpc.ErrorInfo array (@type, reason, domain) used by the JSON-RPC transport, so both transports produce consistent errors.

Testing

  • dotnet test tests/A2A.AspNetCore.UnitTests --framework net8.095 passed, 0 failed (baseline 88, +7 new regression tests: empty-parts rejection for REST send and streaming send with HTTP 400 + error message; SSE error code/message/data preservation for A2AException; -32603 fallback without leaking generic exception messages; no error event on client disconnect; headers; valid data frames).
  • dotnet test tests/A2A.UnitTests --framework net8.0420 passed, 0 failed (unchanged).
  • Behavior change: REST SSE error events now carry code/message/data instead of the hardcoded string. Clients parsing REST SSE errors should read the structured error object; non-error stream frames are unchanged. REST message/send/message:stream now reject requests with empty message parts (HTTP 400) instead of accepting them.

@ez-lbz
ez-lbz force-pushed the fix/rest-validation-sse-errors branch from 0cea223 to 78955d6 Compare August 11, 2026 13:38
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.

1 participant