Skip to content

feat(rest): implement RFC 9457 problem details for errors#771

Open
knapg wants to merge 1 commit into1.0-devfrom
knap-errors
Open

feat(rest): implement RFC 9457 problem details for errors#771
knapg wants to merge 1 commit into1.0-devfrom
knap-errors

Conversation

@knapg
Copy link
Collaborator

@knapg knapg commented Mar 5, 2026

Description

feat(rest): implement RFC 9457 problem details for errors

  • Updates REST error handlers to return RFC 9457 compliant application/problem+json responses.

  • Adds A2AErrorToTypeURI and A2AErrorToTitle mappings for A2A protocol errors.

  • Adds a global StarletteHTTPException handler in the FastAPI app.

  • Wraps the /well-known/agent.json endpoint with rest_error_handler.

  • Updates unit tests to verify the new Problem Details response format.

  • Follow the CONTRIBUTING Guide.

  • Make your Pull Request title in the https://www.conventionalcommits.org/ specification.

    • Important Prefixes for release-please:
      • fix: which represents bug fixes, and correlates to a SemVer patch.
      • feat: represents a new feature, and correlates to a SemVer minor.
      • feat!:, or fix!:, refactor!:, etc., which represent a breaking change (indicated by the !) and will result in a SemVer major.
  • Ensure the tests and linter pass (Run bash scripts/format.sh from the repository root to format)

  • Appropriate docs were updated (if necessary)

Fixes #722 🦕

- Updates REST error handlers to return RFC 9457 compliant `application/problem+json` responses.
- Adds `A2AErrorToTypeURI` and `A2AErrorToTitle` mappings for A2A protocol errors.
- Adds a global StarletteHTTPException handler in the FastAPI app.
- Wraps the `/well-known/agent.json` endpoint with `rest_error_handler`.
- Updates unit tests to verify the new Problem Details response format.

Refs #722
@knapg knapg requested a review from ishymko March 5, 2026 14:48
@knapg knapg requested a review from a team as a code owner March 5, 2026 14:48
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the error handling mechanism for REST endpoints by adopting the RFC 9457 Problem Details standard. This change provides a consistent, machine-readable format for API error responses, improving interoperability and client-side error processing. It centralizes error formatting, ensuring that both application-specific and generic HTTP exceptions adhere to the new standard.

Highlights

  • RFC 9457 Compliance: REST error handlers now return responses compliant with RFC 9457 Problem Details for HTTP APIs, using the application/problem+json media type.
  • Error Mappings: New mappings (A2AErrorToTypeURI and A2AErrorToTitle) have been introduced to provide standardized type URIs and titles for A2A protocol errors within the problem details.
  • Global Exception Handling: A global StarletteHTTPException handler has been added to the FastAPI application to ensure all HTTP exceptions are formatted as RFC 9457 problem details.
  • Endpoint Error Handling: The /well-known/agent.json endpoint is now wrapped with the rest_error_handler to ensure consistent error responses.
  • Test Coverage: Unit tests have been updated and expanded to verify the correct implementation and formatting of the new Problem Details responses for various error scenarios.
Changelog
  • src/a2a/server/apps/rest/fastapi_app.py
    • Imported StarletteHTTPException and rest_error_handler for use in the FastAPI application.
    • Applied the rest_error_handler decorator to the get_agent_card endpoint to standardize its error responses.
    • Implemented a global StarletteHTTPException handler to catch and format all standard HTTP exceptions into RFC 9457 compliant application/problem+json responses.
  • src/a2a/utils/error_handlers.py
    • Adjusted HTTP status codes for several A2A errors (e.g., InvalidParamsError, PushNotificationNotSupportedError, UnsupportedOperationError, AuthenticatedExtendedCardNotConfiguredError) to be 400 instead of 422, 501, or 404.
    • Introduced A2AErrorToTypeURI and A2AErrorToTitle dictionaries to map A2A error types to specific URI identifiers and human-readable titles, respectively.
    • Added a new helper function _build_problem_details_response to construct RFC 9457 compliant JSON responses from A2AError instances.
    • Refactored the rest_error_handler decorator to utilize _build_problem_details_response for A2AError exceptions, centralizing the problem details generation logic.
    • Updated the generic Exception handling within rest_error_handler to also return a standardized application/problem+json response for unknown errors.
  • tests/server/apps/rest/test_rest_fastapi_app.py
    • Imported patch from unittest.mock and InternalError for testing purposes.
    • Added test_global_http_exception_handler_returns_problem_details to verify that standard FastAPI 404 errors are correctly transformed into RFC 9457 format.
    • Included test_get_agent_card_error_handling to confirm that A2AError exceptions raised by the agent card endpoint are caught and formatted as problem details.
  • tests/utils/test_error_handlers.py
    • Imported A2AErrorToTitle and A2AErrorToTypeURI for testing the new error mappings.
    • Modified MockJSONResponse to support a media_type attribute, enabling verification of the application/problem+json header.
    • Updated existing tests for rest_error_handler to assert the correct media_type and the new RFC 9457 compliant structure of error responses.
    • Added test_rest_error_handler_with_data_extensions to ensure that additional data from A2AError instances is correctly included as extension members in the problem details response.
    • Consolidated and expanded test_a2a_error_to_http_status_mapping into test_a2a_error_mappings to include assertions for A2AErrorToTypeURI and A2AErrorToTitle.
Activity
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

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

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 implements RFC 9457 for problem details in error responses, standardizing the error format to enhance the REST API's clarity and predictability for clients. However, the implementation in src/a2a/utils/error_handlers.py introduces a potential information exposure vulnerability by automatically including all fields from the error.data dictionary in the public API response, which could lead to the leakage of sensitive data. Additionally, there is a minor suggestion to improve the robustness of error logging.

Comment on lines +132 to +136
data = getattr(error, 'data', None)
if isinstance(data, dict):
for key, value in data.items():
if key not in payload:
payload[key] = value
Copy link
Contributor

Choose a reason for hiding this comment

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

security-medium medium

The _build_problem_details_response function now includes all fields from the error.data dictionary in the JSON response payload. This can lead to the exposure of internal state or sensitive information that was intended only for logging or internal processing. Previously, only the message and type were exposed. Blindly merging the data dictionary into the response payload violates the principle of least privilege and can result in unintended information disclosure.

Comment on lines +120 to +122
', Data=' + str(getattr(error, 'data', ''))
if getattr(error, 'data', None)
else '',
Copy link
Contributor

Choose a reason for hiding this comment

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

low

The current logic for logging the data attribute of an error will not log falsy values (e.g., an empty dictionary {}), which might be valid data that should be logged. Using hasattr(error, 'data') is a more reliable way to check for the attribute's existence. This suggestion also simplifies the expression using an f-string for better readability.

Suggested change
', Data=' + str(getattr(error, 'data', ''))
if getattr(error, 'data', None)
else '',
f", Data={error.data}" if hasattr(error, 'data') else ''

Copy link
Member

@ishymko ishymko left a comment

Choose a reason for hiding this comment

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

Nice work 👍

Left a few minor comments.

Comment on lines +120 to 123
@rest_error_handler
async def get_agent_card(request: Request) -> Response:
card = await self._adapter.handle_get_agent_card(request)
return JSONResponse(card)
Copy link
Member

Choose a reason for hiding this comment

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

Not sure it should be wrapped here, that's true that it can throw, but .well-known/agent-card.json is "outside" of transport in general, just one per server: 14.3. Well-Known URI Registration.

It's another question why things are wired up like this in the current SDK, I'll take a look separately, let's for now keep "static" agent card as is.

'detail': getattr(error, 'message', str(error)),
}

data = getattr(error, 'data', None)
Copy link
Member

Choose a reason for hiding this comment

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

Maybe let's add an explicit property data to A2AError? I believe that now we don't have it on any of the errors but that's true that the spec gives some good examples on what can be put there, like in 11.6. Error Handling.

We may also put a comment explicitly stating that this data is going to be transmitted unaltered in a transport-specific way (to address this).

}


def _build_problem_details_response(error: A2AError) -> JSONResponse:
Copy link
Member

Choose a reason for hiding this comment

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

nit: maybe this one can return something typed shaped as a problem+json payload so that it can also handle non-A2A errors?

This way the code below

except Exception:
            logger.exception('Unknown error occurred')
            return JSONResponse(
                content={
                    'type': 'about:blank',
                    'title': 'Internal Error',
                    'status': 500,
                    'detail': 'Unknown exception',
                },
                status_code=500,
                media_type='application/problem+json',
            )

won't have to duplicate it JSONResponse(... can be used right away on the return value of this function.

AuthenticatedExtendedCardNotConfiguredError: 400,
}

A2AErrorToTypeURI: dict[_A2AErrorType, str] = {
Copy link
Member

Choose a reason for hiding this comment

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

Here and below: should be possible to only put domain errors inheriting A2AError from the utils/errors.py to properly map them to problem+json.

This list actually reflects what should be handled, various JSONRPCXxx errors below are leftovers from former eras of this.

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.

2 participants