Agent Wranglers

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.