Skip to content

fix: sanitize internal error messages in JSON-RPC and gRPC paths - #1183

Open
ez-lbz wants to merge 7 commits into
a2aproject:mainfrom
ez-lbz:fix/error-message-sanitization
Open

fix: sanitize internal error messages in JSON-RPC and gRPC paths#1183
ez-lbz wants to merge 7 commits into
a2aproject:mainfrom
ez-lbz:fix/error-message-sanitization

Conversation

@ez-lbz

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

Copy link
Copy Markdown

What changed

1. Stop leaking internal exception details in JSON-RPC errors

Problem: The JSON-RPC dispatcher (src/a2a/server/routes/jsonrpc_dispatcher.py) embedded the raw exception text into client-visible errors in four places: base request validation (InvalidRequestError(data=str(e))), param parsing (InvalidParamsError(data=str(e))), the unhandled-exception path (InternalError(message=str(e))), and the SSE stream error path (InternalError(message=str(e))). The generic defensive conversion in _generate_error_response() also used InternalError(message=str(error)). This leaks internal paths, library versions, and stack details to clients (fingerprinting / information disclosure).

Fix (src/a2a/server/routes/jsonrpc_dispatcher.py):

  • All four call sites now return the corresponding A2A error with its default (generic) message; the original exception is still logged server-side via logger.exception(...).
  • _generate_error_response() converts unknown errors to a bare InternalError() ("Internal error") instead of embedding str(error).

2. Stop leaking unknown exception details in the gRPC handler

Problem: GrpcHandler (src/a2a/server/request_handlers/grpc_handler.py) leaked exception details in two ways: non-A2A exceptions were not caught by _handle_unary/_handle_stream and propagated to the gRPC framework with their message text; and the abort_context() fallback sent Unknown error type: {error}, embedding the exception repr.

Fix (src/a2a/server/request_handlers/grpc_handler.py):

  • _handle_unary and _handle_stream now catch generic Exception, log it, and abort with InternalError (INTERNAL / "Internal error").
  • abort_context()'s fallback now logs the unknown error and aborts with a generic "Unknown error" message instead of embedding the error repr.

Testing

  • ./.venv/Scripts/python -m pytest tests/server/routes/test_jsonrpc_dispatcher.py tests/server/request_handlers/test_grpc_handler.py tests/server/test_integration.py -q97 passed (includes new sanitization tests for the unhandled-exception, malformed-params and invalid-base-request paths, plus gRPC unhandled-exception and unknown-A2A-error paths; all assert the internal text does not appear in the response).
  • ./.venv/Scripts/python -m pytest tests/compat/v0_3/ -q250 passed (v0.3 compat unaffected).
  • Updated tests/server/test_integration.py::test_unhandled_exception to assert the generic "Internal error" message and the absence of the exception text (previously it asserted the leaked message).
  • ./.venv/Scripts/python -m ruff check on modified files: clean.
  • Behavior change: non-A2A exception messages are no longer sent to clients over JSON-RPC or gRPC; clients now receive Internal error / Unknown error. Error codes are unchanged.

@ez-lbz
ez-lbz requested a review from a team as a code owner August 10, 2026 16:10
ez-lbz added 5 commits August 11, 2026 00:40
Agent exceptions are now sanitized server-side (internal details are
logged, not exposed to clients). The integration scenario that asserted
the raw exception message leaks to the client is updated to expect the
generic InternalError instead, catching the base A2AError so the check
holds across transports.
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown

🧪 Code Coverage (vs main)

⬇️ Download Full Report

Base PR Delta
src/a2a/client/transports/grpc.py 92.75% 91.30% 🔴 -1.45%
src/a2a/server/events/event_queue_v2.py 91.79% 91.28% 🔴 -0.51%
src/a2a/server/request_handlers/grpc_handler.py 95.42% 96.88% 🟢 +1.45%
src/a2a/utils/telemetry.py 91.47% 90.70% 🔴 -0.78%
Total 93.00% 92.98% 🔴 -0.02%

Generated by coverage-comment.yml

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