# HTTP Error Docs in 2026: RFC 9110 vs RFC 9457 Problem Details in OpenAPI 3.1

Brady Weaver · October 29, 2026

> How RFC 9110 and RFC 9457 divide HTTP error docs: status-code semantics vs Problem Details bodies, plus one reusable OpenAPI 3.1 error component.

| Takeaway | Detail |
| --- | --- |
| Split RFC 9110 status-code semantics from RFC 9457 Problem Details bodies. | Thesis rule: RFC 9110 owns the status-code semantics layer; RFC 9457 owns the machine-readable body layer. |
| Define one reusable Problem Details component with type, title, status, detail, instance, and extensions. | Reader rule: put the listed fields in a single OpenAPI 3.1 component rather than duplicating them per response. |
| Reference that shared Problem schema from every error response. | Reader rule: every error response uses the component reference and never restates status-code semantics. |
| Treat 200-to-201 and 200-to-202 response transitions as breaking changes. | Azion: status-code semantics should not change between API versions; document 200→201 or 200→202 as breaking. |

This reference guide separates RFC 9110 status-code semantics from RFC 9457 Problem Details bodies for OpenAPI 3.1 error documentation. It shows how to define one reusable Problem Details component — type, title, status, detail, instance, and extensions — and reference it from every error response without restating status-code semantics.

![HTTP Error Docs in 2026](https://static.mm-ais.com/article-images-ai/http-error-docs-in-2026-rfc-9110-vs-rfc-ai-d49a40a6.jpg)

## Two Layers: RFC 9110 vs RFC 9457

Every HTTP error response actually carries two independent layers of meaning, and the specs split ownership cleanly between them. RFC 9110, HTTP Semantics, governs the status-code layer: what 404 means at the protocol level, whether a 503 response is retry-eligible, how caches treat a 410. RFC 9457, Problem Details for HTTP APIs, governs the body layer: the JSON object your API returns alongside that status code, with its defined members and extension points. Neither spec owns the other's territory, and confusing them is the root cause of most error-documentation drift.

RFC 9110 obsoleted the older RFC 7230–7231 series and consolidated HTTP semantics into one document. Its status-code definitions are not documentation suggestions — they are protocol contracts. As the Azion deep read (published 2026-06-17) puts it, status codes are "machine-readable signals that intermediaries and clients use to determine caching behavior, retry eligibility." That means a proxy deciding whether to cache a response, or a client SDK deciding whether to retry, is reading the status code — not your JSON body. Change the meaning of a status code and you change behavior for every intermediary on the path.

RFC 9457, published in 2024, obsoletes RFC 7807 and standardizes the machine-readable error body: a small set of defined members plus extension fields your API can add. The body is for humans and client code that parses errors; the status code is for the protocol machinery. The two layers answer different questions for different readers, which is exactly why they need separate documentation.

The practical check this gives you: audit every error response in your OpenAPI document and ask two questions separately. Does the status code match its RFC 9110 semantics — is a 200-to-201 or 200-to-202 transition documented as a breaking change, as Azion recommends? And does the body conform to your Problem Details schema? A response can pass one check and fail the other; documenting them as one undifferentiated blob guarantees you'll miss the failure.

Keep the division of labor explicit in your own writing and reviews: status-code semantics belong to RFC 9110, response-body schema belongs to RFC 9457, and your OpenAPI document should reference a single reusable Problem component for the second layer while letting the status-code enum speak for the first. When a reviewer proposes "just describe the error in prose," push back by naming the layer the prose would duplicate — the protocol layer is already specified, and the body layer is already schematized.

![Two Layers: RFC 9110 vs RFC 9457 — HTTP Error Docs in 2026](https://static.mm-ais.com/article-images-ai/http-error-docs-in-2026-rfc-9110-vs-rfc-ai-3a859a0a.jpg)

## How OpenAPI 3.1 Encodes Errors

OpenAPI 3.1 is where the reusable Problem component stops being a convention and becomes enforceable schema. The specification adopted JSON Schema 2020-12 in place of the subset that 3.0 carried, which means you can express oneOf, anyOf, and $ref directly in your error bodies without the 3.0-era workarounds for nullable fields and file payloads. That matters for error documentation because Problem Details bodies are rarely flat: a validation failure carries a different extension set than a rate-limit failure, and 2020-12 lets you model both under one schema. Before you commit, test your codegen tool against a minimal 3.1 document that uses a $ref inside a response schema — generator support for 3.1 is uneven, and a tool that silently downgrades to 3.0 semantics will strip exactly the constructs you relied on.

The reusable pattern has three moves. First, define components.schemas.Problem with the RFC 9457 members — type, title, status, detail, instance — plus your extension fields, and mark only type and title as required where your contract allows. Second, define components.responses.Problem with content: application/problem+json and a $ref to that schema. Third, reference the response component from every operation's 4XX and 5XX entries rather than inlining a body. One edit to the schema then propagates to every endpoint in the document, which is the mechanism that drives drift toward zero: there is no second copy to fall out of sync.

Verify the wiring rather than trusting it. Lint the document for operations that declare an error status without a $ref to the shared response — those are your drift candidates. Confirm the media type on the response component is application/problem+json and not application/json, since a mismatched media type breaks content negotiation at the client. Then generate a client from the document and inspect the emitted error type: if the generator produced a distinct class per endpoint instead of one shared type, the $ref chain is broken somewhere upstream.

| Layer | Owner | What you document | Where it lives in 3.1 |
| --- | --- | --- | --- |
| Status-code semantics | RFC 9110 | What the code means at the protocol level | Operation response keys (4XX, 5XX) |
| Machine-readable body | RFC 9457 | Problem members and extensions | components.schemas.Problem |

Keep the two layers separate in the document even though they meet in the same response object. The status key carries the RFC 9110 meaning; the $ref carries the RFC 9457 body. Azion's guidance on status-code semantics is blunt about the consequence of blurring them: status code semantics should not change between API versions, and a transition such as 200 to 201 or 200 to 202 belongs in the breaking-change log, not in a quietly edited description. A shared Problem schema referenced from every error response gives you one place to change the body and no place to accidentally redefine the code.

![How OpenAPI 3.1 Encodes Errors — HTTP Error Docs in 2026](https://static.mm-ais.com/article-images-pixabay/http-error-docs-in-2026-rfc-9110-vs-rfc-3c60219d.jpg)

## The Evidence: What the Specs Actually Say

The strongest argument for splitting error documentation into two layers is not a style preference; it is what the specifications themselves say when you read their structure. RFC 9110, HTTP Semantics, devotes Sections 6 through 9 to message abstraction, routing, representation data and metadata, and method and status-code semantics. That ordering is the tell: status codes sit inside a protocol-level framework that governs how intermediaries, caches, and clients interpret a response, not a menu of API-design suggestions. Section 2, Conformance, then draws the line between requirements expressed as MUST and recommendations expressed as SHOULD. Your documentation should mirror that distinction rather than flatten it — if the spec says a sender MUST include a particular header on a given status, your error reference should say the same thing in the same normative register.

The Azion learning article on HTTP status codes, published 2026-06-17, makes the operational consequence explicit: status codes are three-digit response codes defined in RFC 9110 that encode the result of a request's processing, and at the protocol level they are machine-readable signals that intermediaries and clients use to determine caching behavior and retry eligibility. The same article states that status code semantics should not change between API versions, and that a response transitioning from 200 to 201 or from 200 to 202 should be documented as a breaking change. That is a check you can run against your own changelog: scan release notes for any status-code reassignment and confirm each one is flagged as breaking, because the code is part of the API contract, not an implementation detail.

The third source is the RFC Editor's published text of RFC 9110 itself, which carries the same structural claim in its table of contents and its conformance language. Read together, the three sources converge on one rule for documentation authors: cite the status code's meaning from RFC 9110 and cite the body's shape from RFC 9457, and keep the two citations in separate fields of your reference page. When a reviewer asks why a 503 is documented as retryable, the answer should point at the semantics section of RFC 9110; when they ask what fields the client will receive, the answer should point at your Problem schema.

A practical audit follows from this. For every error response in your OpenAPI 3.1 document, confirm two things are present and separately traceable: a status code whose description quotes or paraphrases RFC 9110 semantics, and a body schema that resolves to your single shared Problem component. If either is missing, or if the status description restates body fields, you have drift. The fix is deletion, not addition — remove the duplicated prose and leave one authoritative pointer per layer.

| Source | What it establishes | Documentation check |
| --- | --- | --- |
| RFC 9110 (httpwg.org) | Sections 6–9 place status codes within message, routing, representation, and method semantics; Section 2 separates MUST from SHOULD | Status descriptions use normative language matching the spec's requirement level |
| RFC 9110 (RFC Editor) | Published normative text of the same specification | Every semantic claim in your error reference cites a section, not a blog post |
| Azion, 2026-06-17 | Status codes are RFC 9110-defined signals for intermediaries and clients; semantics should not change across API versions; 200→201 or 200→202 is a breaking change | Changelog scan flags every status reassignment as breaking |

![The Evidence: What the Specs Actually Say — HTTP Error Docs in 2026](https://static.mm-ais.com/article-images-pixabay/http-error-docs-in-2026-rfc-9110-vs-rfc-4f50dbe8.jpg)

## Options Compared: Three Doc Strategies

Three workable strategies exist for documenting errors in an OpenAPI 3.1 document, and they are not equivalent. The choice comes down to where each piece of information lives and how many places it can silently diverge.

**Strategy A — inline prose per endpoint.** Every operation lists its own error responses with hand-written descriptions: "404: the resource was not found," "422: validation failed," and so on, repeated across dozens of operations. This is the default most teams fall into, and it has the highest drift risk by construction. There is no single source of truth, so when someone updates the 404 description on one endpoint and forgets the other eleven, the document contradicts itself — and nothing in the tooling flags it.

**Strategy B — shared Problem component plus RFC 9110 links.** One components.responses entry defines the reusable Problem Details shape, every error response references it, and status-code semantics are pointed at the relevant RFC 9110 sections rather than restated in prose. API-specific fields ride along as extensions on the base schema. This is the winner: the body layer is machine-readable, the status layer is delegated to the spec that owns it, and drift has nowhere to hide because each fact is written exactly once.

**Strategy C — hybrid.** The shared component governs response bodies, but each endpoint keeps its own inline status prose. This is acceptable only when an endpoint genuinely has semantics that differ from the standard meaning of the code — and per the Azion guidance on status-code semantics, those codes are part of the API contract and should not shift meaning between versions, so any such deviation must be documented as a deliberate, breaking change. Outside that narrow case, the hybrid just reintroduces the drift surface Strategy A suffers from, one endpoint at a time.

What the evidence does not establish is the size of the effect. No controlled study appears in these sources measuring documentation drift before and after adopting a shared component; the case rests on mechanism — a single referenced schema cannot disagree with itself, while prose restated per response can. Treat "near zero" as a structural expectation to verify in your own repository rather than a measured result, and audit it by diffing documented error responses against traffic captured from your test suite.

![sea nature seagull bird flight doc](https://static.mm-ais.com/article-images-pixabay/http-error-docs-in-2026-rfc-9110-vs-rfc-66043f8d.jpg)
sea nature seagull bird flight doc

## Worked Example: Run the Numbers

Take one concrete case: a payments API team documenting errors for a public API version, with 40 documented error responses spread across 12 endpoints. Under its old inline-prose strategy, each error response restated the status-code meaning in a description field and carried a hand-written body example — 40 descriptions and 40 examples, or 80 places where a status code's meaning could drift from the spec. The question this example answers is narrow: how many of those 80 restatement points survive once the team adopts a single shared Problem component referenced from every error response?

Step one is the inventory. Count every error response in the document and every place its status-code semantics are restated outside the status code itself. Here that is 40 responses × 2 restatement points = 80. Step two is the consolidation. Each error response keeps its status code and its $ref to the shared Problem schema; the prose restatement of what the code means is deleted, because RFC 9110 already owns that meaning and the reader can follow the reference. That removes 40 description restatements. Step three is the body layer: the 40 hand-written examples collapse to one Problem schema definition plus per-response extension examples, so the example surface drops from 40 to 1 schema. The arithmetic: 80 restatement points fall to 40 (the status codes themselves, which are not restatements) plus 1 shared schema — a reduction of 39 of the 80 original points, or 48.75 percent, with the remaining 40 being the codes the spec requires you to declare anyway. These figures are an illustration of the counting method, not a measured result; plug in your own endpoint and error-code counts before trusting the conclusion.

mark from any study.

The break-even trigger is equally concrete. The consolidation costs a fixed setup: one Problem component with type, title, status, detail, instance, and extension slots, plus 40 $ref edits. The drift savings accrue per restatement point removed per revision cycle. If the team revises error documentation twice a year and each revision previously touched a share of the 80 points, the setup pays back as soon as cumulative touched points exceed the setup count — for this example, the first revision cycle that touches more than the setup's equivalent number of points clears the threshold. Track it as a ratio: restatement points removed divided by setup edits; a ratio above 1.0 at the first review means the shared component is already ahead.

Declare the winner for this example: the shared-component strategy, at 48.75 percent fewer restatement points and a single body schema to maintain. The loser is not the hybrid — it is any strategy that leaves status-code meaning restated in prose, because those 40 description points are exactly where RFC 9110 semantics and the document diverge. The check to run before March 2: grep the document for status-code numbers appearing inside description text; every hit is a drift candidate the shared component would have eliminated.

| Measure | Inline prose | Shared component |
| --- | --- | --- |
| Error responses | 40 | 40 |
| Status-code restatements in prose | 40 | 0 |
| Hand-written body examples | 40 | 1 schema |
| Total restatement points | 80 | 41 |
| Reduction | — | 39 points (48.75%) |

![the sea marina water harbour sky the ocean ship doc nature travel boat port sunset kay transport architecture](https://static.mm-ais.com/article-images-pixabay/http-error-docs-in-2026-rfc-9110-vs-rfc-03c80d49.jpg)
the sea marina water harbour sky the ocean ship doc nature travel boat port sunset kay transport architecture

## And Copy-Usable Template

Here is the full template applied to the simplest error most APIs ship: a 404 on GET /orders/{id} when the order does not exist. The status-code layer is defined by HTTP semantics, so you write nothing new there; the body layer is your Problem schema; and the only endpoint-specific content is a short description and the detail string. Work through the three checkpoints in order, and this operation is done.

| Layer | Field | Value | Owned by |
| --- | --- | --- | --- |
| Status code | — | 404 | HTTP semantics (RFC 9110) — do not restate |
| Body | type | https://api.example.com/errors/not-found | Your Problem component |
| Body | title | Order not found | Your Problem component |
| Body | status | 404 | Your Problem component |
| Body | detail | No order with id {id} | Endpoint-specific only |
| Body | instance | /orders/{id} | Endpoint-specific only |

Checkpoint 1 — schema check. The operation's 404 response contains a single $ref to components.responses.Problem and nothing else. Run your validator: if it flags any inline error description, any duplicated content block, or any schema field not defined in the shared component, the drift the reusable-component strategy is designed to prevent has already started. A passing validator with zero inline error bodies is the pass condition.

Checkpoint 2 — semantics check. Read only the description field. It must say what is endpoint-specific: when this operation returns 404 (the order identified by {id} does not exist) and nothing about what 404 means in general. If the description contains the phrase "resource not found," a list of common client causes, or any protocol-level explanation, that text belongs in the shared component's documentation — or nowhere — because it will drift from the spec's meaning. A working rule: if the sentence would still be true on a different endpoint, it is in the wrong place.

Checkpoint 3 — consistency check. Confirm the machine-readable body agrees with the wire. The status member must equal the actual status code (404 here), the type URI must resolve to a human-readable page, and instance must echo the request path with the identifier substituted. This is the cross-layer check the two-spec division implies: a mismatch between the status code and the body's status member is exactly the class of error that separate documentation produces and a single referenced schema catches at validation time.

Every other error on every other endpoint follows the same shape. Fill the table's two endpoint-specific rows, pass the three checkpoints, and move on — the remaining fields are already written once, in one place, for the whole API.

## What to do next

| Step | Action | Why it matters |
| --- | --- | --- |
| 1 | Open your OpenAPI 3.1 document and define a single reusable Problem Details component containing type, title, status, detail, instance, and your extensions. | This is the canonical decision rule: one component, not per-response duplication. |
| 2 | Replace every inline error schema with a reference to that shared Problem Details component. | A single source of truth keeps error bodies consistent across all endpoints. |
| 3 | Remove any prose that restates status-code semantics RFC 9110 already defines, and link to it instead. | RFC 9110 owns the status-code semantics layer; your doc should not duplicate it. |
| 4 | Verify each error body follows RFC 9457 Problem Details structure, so the machine-readable layer stays separate from the status-code layer. | The takeaway split: RFC 9110 for semantics, RFC 9457 for bodies. |
| 5 | Audit your API versions for any 200-to-201 or 200-to-202 response transitions and mark them as breaking changes in your changelog. | Status-code semantics should not change between API versions — documenting 200→201 or 200→202 as breaking protects consumers. |
| 6 | Re-check the evolution timeline above — from the 2020 groundwork through the 2024 convergence to your 2026 target — and confirm every error response now uses the component reference. | Keeps your documentation aligned with the current standards landscape instead of the legacy 7230/7231-era habits. |

## Frequently Asked Questions

**Do RFC 9110 and RFC 9457 cover the same things in an error response?**

No — RFC 9110 owns the status-code semantics layer, while RFC 9457 owns the machine-readable body layer.

**Which fields should go into a reusable Problem Details component in OpenAPI 3.1?**

Define a single component with type, title, status, detail, instance, and extensions.

**Should I repeat status-code semantics in each error response definition?**

No — every error response should reference the shared Problem schema component and never restate status-code semantics.

**Is changing a 200 response to a 201 in a new API version a breaking change?**

Yes — Azion treats 200→201 or 200→202 response transitions as breaking changes because status-code semantics should not change between API versions.

**Where should the Problem Details fields live — per response or in one place?**

Put the listed fields in a single OpenAPI 3.1 component rather than duplicating them per response.

**How many layers of meaning does an HTTP error response carry?**

Two independent layers — the status-code layer governed by RFC 9110 and the body layer governed by RFC 9457, with ownership split cleanly between the specs.

## Quick answers

| Which RFC governs status-code semantics in OpenAPI 3.1 error documentation? | RFC 9110, HTTP Semantics, governs the status-code layer. |
| --- | --- |
| Which RFC owns the machine-readable error body layer? | RFC 9457, Problem Details, owns the machine-readable body layer. |
| What fields should the reusable Problem Details component define? | It should define type, title, status, detail, instance, and extensions in a single OpenAPI 3.1 component. |
| How should error responses reference the Problem schema? | Every error response should use the shared component reference and never restate status-code semantics. |
| How are 200-to-201 or 200-to-202 response transitions treated? | They should be documented as breaking changes, since status-code semantics should not change between API versions. |

Also worth reading: **Nullable fields in schemas: OpenAPI 3.1.0 type arrays vs nullable**: [Nullable fields in schemas: OpenAPI](https://specswriter.com/blog/nullable-fields-in-schemas-openapi-310-type-arrays-vs-nullable.php) · **How to document null values: OpenAPI 3.1.0 drops nullable — use type: null?**: [How to document null values:](https://specswriter.com/blog/how-to-document-null-values-openapi-310-drops-nullable-use-type-null.php) · **OpenAPI 4.0's nullable: A Parse Failure Disguised as Spec Upgrade**: [OpenAPI 4.0's nullable: A Parse](https://specswriter.com/blog/openapi-40s-nullable-a-parse-failure-disguised-as-spec-upgrade.php)

### Related reading

- [How to document null values: OpenAPI 3.1.0 drops nullable — use type: null?](https://specswriter.com/blog/how-to-document-null-values-openapi-310-drops-nullable-use-type-null.php)
- [OpenAPI 3: One Nullable Keyword Deleted, Three Encodings Win](https://specswriter.com/blog/openapi-3-one-nullable-keyword-deleted-three-encodings-win.php)
- [OpenAPI 4.0's nullable: A Parse Failure Disguised as Spec Upgrade](https://specswriter.com/blog/openapi-40s-nullable-a-parse-failure-disguised-as-spec-upgrade.php)
- [The Psychology of Customer-Centric Sales Shifting Focus from Product to Problem-Solving](https://specswriter.com/blog/the_psychology_of_customer_centric_sales_shifting_focus_from.php)
- [Mastering the Art of Problem Statements A Key to Compelling Grant Proposals](https://specswriter.com/blog/mastering_the_art_of_problem_statements_a_key_to_compelling.php)
- [DeepMind's SELFDISCOVER Framework Boosts LLM Reasoning by 32%, Mimicking Human Problem-Solving](https://specswriter.com/blog/deepmind_s_selfdiscover_framework_boosts_llm_reasoning_by_32.php)

### Latest

- [How to document null values: OpenAPI 3.1.0 drops nullable — use type: null?](https://specswriter.com/blog/how-to-document-null-values-openapi-310-drops-nullable-use-type-null.php)
- [Nullable fields in schemas: OpenAPI 3.1.0 type arrays vs nullable](https://specswriter.com/blog/nullable-fields-in-schemas-openapi-310-type-arrays-vs-nullable.php)
- [Task test results: 2026 AI vs Darwin Information Typing Architecture (DITA)...](https://specswriter.com/blog/task-test-results-2026-ai-vs-darwin-information-typing-architecture-dita-1331-adopt.php)

Canonical: https://specswriter.com/blog/http-error-docs-in-2026-rfc-9110-vs-rfc-9457-problem-details-in-openapi-31.php
Markdown: https://specswriter.com/blog/http-error-docs-in-2026-rfc-9110-vs-rfc-9457-problem-details-in-openapi-31.php/index.md
