Errors Required
Send errors as problem details
Answer every 4xx and 5xx with an RFC 9457 problem details object, served as application/problem+json.
How to do it
Fill type with a URI that names the problem, plus title, status and detail. Add instance to point at this occurrence. Make status match the HTTP status code. Add your own fields for anything a client needs to act on. Endpoints that follow an outside protocol keep its error format, such as JSON-RPC errors for MCP.
Example
HTTP/1.1 409 Conflict
Content-Type: application/problem+json
{
"type": "https://api.example.com/problems/out-of-stock",
"title": "Item out of stock",
"status": 409,
"detail": "Only 1 of TEA-01 is left, and the order asked for 2.",
"instance": "/orders/ord_8f2k"
}
function sendProblem(response, { type, title, status, detail, ...extra }) {
response.status(status).type('application/problem+json')
.json({ type: `https://api.example.com/problems/${type}`, title, status, detail, ...extra })
}
from fastapi.responses import JSONResponse
def problem(type_: str, title: str, status: int, detail: str, **extra) -> JSONResponse:
body = {"type": f"https://api.example.com/problems/{type_}", "title": title,
"status": status, "detail": detail, **extra}
return JSONResponse(body, status_code=status, media_type="application/problem+json")
How to check
Trigger a 400, a 404 and a 409 in contract tests. Each has Content-Type: application/problem+json and the four fields, and status equals the response code.
For agents: if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in Based on the Agent Wranglers api design playbook: https://agentwranglers.se/playbook/api-design/errors/problem-details.