---
title: "RFC 9457 Problem Details"
description: "The current standard for structured HTTP error responses that agents can parse and act on"
url: "https://agentsurface.dev/docs/error-handling/rfc-9457"
lastVerified: 2026-09-24
lastModified: 2026-09-25T05:46:01.000Z
---



## Summary [#summary]

RFC 9457 standardizes error response format with five core fields: type (stable URI), title, status, detail, instance. Agents branch on type, never on title or detail. Extensions like is\_retriable, retry\_after\_ms, suggestions, trace\_id, doc\_uri enable autonomous recovery.

```
{
  "type": "https://api.example.com/errors/rate-limit-exceeded",
  "title": "Rate limit exceeded",
  "status": 429,
  "is_retriable": true,
  "retry_after_ms": 5000,
  "suggestions": ["Wait 5 seconds and retry"]
}
```

* type: stable URI agents can branch on (contract-level)
* is\_retriable: explicit boolean, not inferred from status code
* suggestions: concrete next steps, first is most likely fix
* trace\_id: correlation ID for internal lookup
* Content-Type: application/problem+json signals RFC 9457 format

RFC 9457 (July 2023) defines a standard JSON format for HTTP error responses. It obsoletes RFC 7807 (2016). Like its predecessor, it answers a fundamental problem: HTTP status codes alone are insufficient for agents to decide whether to retry, modify the request, or escalate.

The format has become widespread. The `application/problem+json` content type signals that the response body follows the standard, allowing agents and client libraries to handle errors consistently regardless of which service returned them.

## The Standard Fields [#the-standard-fields]

```json
{
  "type": "https://example.com/errors/insufficient-credit",
  "title": "Insufficient credit",
  "status": 422,
  "detail": "The account has $12.00 remaining but the transaction costs $42.00.",
  "instance": "/accounts/acme/transactions/txn_01HV3K8MNP"
}
```

| Field      | Required    | Description                                                                                        |
| ---------- | ----------- | -------------------------------------------------------------------------------------------------- |
| `type`     | Recommended | A URI that uniquely identifies the error type. Should dereference to human-readable documentation. |
| `title`    | Recommended | A short, human-readable summary of the problem type. Does not change between occurrences.          |
| `status`   | Optional    | The HTTP status code. Mirrors the response's actual status code.                                   |
| `detail`   | Optional    | A human-readable explanation specific to this occurrence of the problem.                           |
| `instance` | Optional    | A URI reference that identifies the specific occurrence of the problem.                            |

The `type` URI is the stable, parseable identifier that agents can branch on:

```typescript
const response = await fetch("/accounts/acme/charge", {
  method: "POST",
  body: JSON.stringify({ amount: 4200 }),
});

if (!response.ok) {
  const problem = await response.json();

  switch (problem.type) {
    case "https://example.com/errors/insufficient-credit":
      // Specific recovery: check balance, reduce amount, or escalate
      break;
    case "https://example.com/errors/account-suspended":
      // Not recoverable: stop and report to human
      break;
    case "https://example.com/errors/validation-error":
      // Fix the request and retry
      break;
    default:
    // Unknown error type: log and escalate
  }
}
```

Agents should never branch on the `title` or `detail` fields - those are prose for humans and may change between releases. `type` is the stable contract.

## RFC 9457 vs RFC 7807 [#rfc-9457-vs-rfc-7807]

RFC 9457 retains full backward compatibility with RFC 7807 while adding:

* **Formal type registration**: The [IANA HTTP Problem Types registry](https://www.iana.org/assignments/http-problem-types) gives `type` URIs a place to be registered and documented. It covers the `type` values themselves, not extension member names like `is_retriable` or `trace_id` - there is no registry for those; document them in your own API reference.
* **Internationalization support**: Extensions may include language tags and localized explanations.
* **Security clarifications**: Explicit guidance on preventing information disclosure (no stack traces, internal error codes sanitized).
* **Instance URI semantics**: Clearer definition of when `instance` should be a server-relative URI vs absolute URI.

For practical purposes, both standards share the core five fields and the extension mechanism. Use RFC 9457 as the baseline for new APIs.

## Recommended Extensions for Agent APIs [#recommended-extensions-for-agent-apis]

The RFC explicitly allows extension fields beyond the five core ones. For agent-readable APIs, these extensions carry the structured recovery information agents need to decide whether to retry unchanged, modify and retry, or escalate. They are not part of the RFC itself and have no IANA registry of their own - document them in your own API reference instead.

| Field            | Type        | Purpose                                              |
| ---------------- | ----------- | ---------------------------------------------------- |
| `is_retriable`   | `boolean`   | Retry the same request unchanged?                    |
| `retry_after_ms` | `number`    | Milliseconds to wait before retrying                 |
| `doc_uri`        | `string`    | Link to detailed error documentation                 |
| `trace_id`       | `string`    | Correlation ID for internal lookup                   |
| `suggestions`    | `string[]`  | Ordered recovery steps (first is the most likely)    |
| `errors`         | `Problem[]` | Nested Problem Details for batch / multi-field cases |

### `is_retriable` [#is_retriable]

A boolean that explicitly tells the agent whether retrying the same request unchanged might succeed. `false` marks a terminal error where the request itself is wrong - do not infer retriability from the status code alone:

```json
{
  "type": "https://example.com/errors/service-unavailable",
  "title": "Service temporarily unavailable",
  "status": 503,
  "detail": "The invoices service is undergoing maintenance.",
  "is_retriable": true,
  "retry_after_ms": 30000
}
```

### `retry_after_ms` [#retry_after_ms]

How long to wait before retrying, in milliseconds. It is the numeric equivalent of the `Retry-After` header, carried in the body for clients that process the body but not headers - and preferred over the header for parsing consistency. Agents can sleep on it directly:

```typescript
if (error.is_retriable && error.retry_after_ms) {
  await sleep(error.retry_after_ms);
  return retry();
}
```

```json
{
  "type": "https://example.com/errors/rate-limit-exceeded",
  "title": "Rate limit exceeded",
  "status": 429,
  "detail": "You have exceeded the 100 requests per minute limit.",
  "is_retriable": true,
  "retry_after_ms": 47000
}
```

### `suggestions` [#suggestions]

An array of actionable next steps, ordered so the first is the most likely fix. Short, imperative strings describing what the agent should try:

```json
{
  "type": "https://example.com/errors/validation-error",
  "title": "Validation failed",
  "status": 422,
  "detail": "The request body failed schema validation.",
  "suggestions": [
    "Provide a value for the required 'amount' field",
    "The 'currency' field must be a 3-letter ISO 4217 code (e.g., 'USD')"
  ]
}
```

### `doc_uri` [#doc_uri]

A link to detailed documentation for this error type. Unlike `type`, which is a stable machine identifier, `doc_uri` may point to versioned docs or a troubleshooting guide a human can follow up on:

```json
{
  "type": "https://example.com/errors/webhook-validation-failed",
  "title": "Webhook signature validation failed",
  "status": 401,
  "detail": "The X-Webhook-Signature header does not match the expected HMAC-SHA256 signature.",
  "doc_uri": "https://docs.example.com/webhooks/security#signature-validation"
}
```

### `trace_id` [#trace_id]

A correlation ID linking this error to a specific request in the service's observability stack - server logs and OpenTelemetry spans. When an agent escalates to a human, the human or support team can look up the trace without reproducing the issue:

```json
{
  "type": "https://example.com/errors/internal-error",
  "title": "Internal server error",
  "status": 500,
  "detail": "An unexpected error occurred. The issue has been logged.",
  "trace_id": "01HV3K8MNP2QRS3TUVWX",
  "is_retriable": true,
  "retry_after_ms": 5000
}
```

### `errors` [#errors]

RFC 9457 allows aggregating multiple problems under an `errors` array - useful for batch operations or multi-field validation. Each element follows the same Problem Details schema, so an agent can recurse into it to recover per item:

```json
{
  "type": "https://example.com/errors/batch-failure",
  "title": "Batch operation partially failed",
  "status": 422,
  "detail": "3 of 5 items failed validation",
  "errors": [
    {
      "type": "https://example.com/errors/validation-error",
      "detail": "Item 0: 'amount' is required",
      "is_retriable": true
    },
    {
      "type": "https://example.com/errors/validation-error",
      "detail": "Item 2: 'currency' is invalid",
      "is_retriable": true
    }
  ]
}
```

## Validating Problem Details [#validating-problem-details]

A single schema keeps error responses consistent across API handlers and CLI output. This Zod schema covers the core fields plus the agent extensions:

```typescript
import { z } from "zod";

const ProblemDetails = z.object({
  type: z.string().url().optional().default("about:blank"),
  title: z.string(),
  status: z.number().int().min(400).max(599),
  detail: z.string(),
  instance: z.string().optional(),
  is_retriable: z.boolean().optional(),
  retry_after_ms: z.number().int().optional(),
  doc_uri: z.string().url().optional(),
  trace_id: z.string().optional(),
  suggestions: z.array(z.string()).optional(),
  errors: z.array(z.lazy(() => ProblemDetails)).optional(),
});

type ErrorResponse = z.infer<typeof ProblemDetails>;
```

See [`templates/errors-and-auth/problem-details.ts`](/templates/errors-and-auth/problem-details.ts) for a production-ready implementation.

## Handling Problem Details in an Agent [#handling-problem-details-in-an-agent]

Agents branch twice: first on `type` to identify which error occurred (shown above), then on `is_retriable` to decide what to do about it. The recovery branch reads the extension fields:

```typescript
async function callTool(tool: string, params: Record<string, unknown>): Promise<unknown> {
  const response = await fetch(`/api/tools/${tool}`, {
    method: "POST",
    body: JSON.stringify(params),
  });

  if (!response.ok) {
    const error = await response.json();

    if (error.is_retriable === false) {
      // Terminal error - escalate or fail the task
      throw new Error(`${error.title}: ${error.detail}. See ${error.doc_uri}`);
    }

    if (error.is_retriable && error.retry_after_ms) {
      // Transient error with explicit wait time
      await sleep(error.retry_after_ms);
      return callTool(tool, params); // Retry unchanged
    }

    if (error.suggestions && error.suggestions.length > 0) {
      // Recoverable with modification - agent rewrites params
      // (implementation depends on agent framework)
    }
  }

  return response.json();
}
```

## Batch Error Aggregation [#batch-error-aggregation]

When processing multiple items, return partial success with per-item errors under the `errors` array rather than failing the whole batch:

```typescript
async function batchCreateInvoices(invoices: InvoiceData[]): Promise<Response> {
  const results = [];
  const errors = [];

  for (let i = 0; i < invoices.length; i++) {
    try {
      results.push(await createInvoice(invoices[i]));
    } catch (err) {
      errors.push({
        type: "https://example.com/errors/validation-error",
        title: "Validation failed",
        status: 422,
        detail: `Item ${i}: ${err.message}`,
        instance: `batch-item-${i}`,
        is_retriable: true,
      });
    }
  }

  if (errors.length > 0) {
    return Response.json(
      {
        type: "https://example.com/errors/batch-failure",
        title: "Batch operation partially failed",
        status: 207, // Multi-status
        detail: `${results.length} succeeded, ${errors.length} failed`,
        errors,
      },
      { status: 207 },
    );
  }

  return Response.json(results);
}
```

## Cancellation and Intent Tracing [#cancellation-and-intent-tracing]

When an agent cancels an in-flight request, the error can carry an `intent` object describing the operation's state so the agent can reason about cleanup instead of blindly retrying:

```json
{
  "type": "https://example.com/errors/operation-cancelled",
  "title": "Operation Cancelled",
  "status": 408,
  "detail": "Request aborted by client after 5000ms",
  "trace_id": "span-xyz789",
  "is_retriable": true,
  "intent": {
    "operation": "create_invoice",
    "resource_id": "inv-pending-123",
    "state_before_cancel": "payment_processing",
    "recovery": "Operation may be in progress server-side. Poll GET /invoices/inv-pending-123 to check status."
  }
}
```

## Content Type [#content-type]

Always include the `Content-Type: application/problem+json` header on error responses. This signals to clients and agents that the body is a parseable problem document:

```typescript
function problemResponse(res: Response, status: number, problem: ProblemDetails): void {
  res.status(status).setHeader("Content-Type", "application/problem+json").json(problem);
}
```

## Checklist [#checklist]

* [ ] All error responses use `application/problem+json` content type
* [ ] Every error has a stable `type` URI that agents can branch on
* [ ] `is_retriable` is present on every error response
* [ ] `title` is stable; `detail` is occurrence-specific
* [ ] Stack traces and internal details never appear in responses

## Related Pages [#related-pages]

* [Designing Errors for Agent Recovery](/docs/error-handling/errors-for-agents) - structuring error content to enable recovery
* [Retry Patterns](/docs/error-handling/retry-patterns) - how agents should act on error information
* [Templates: Problem Details Schema](/templates/errors-and-auth/problem-details.ts) - Zod implementation
