In an RFC 9457 problem details response, what is the type member for, and how should you choose its URI — does it have to be a URL that resolves?
answer
- type = URI = globally namespaced stable key
- need not resolve; about:blank is the default
- recommended: HTTPS doc page you control
- clients compare opaque strings — never fetch (SSRF)
- per-occurrence identity belongs in instance
basics
~20 sThe type member is a URI identifying the kind of problem — the stable key clients branch on. It need not be dereferenceable, but a resolvable HTTPS documentation URL is recommended. Clients must compare it as an opaque string, never fetch it at runtime.
solid answer
~50 s`type` is the machine-readable discriminator. A URI is used rather than a bare token so identifiers are globally namespaced: two APIs can both have a "conflict" problem without colliding. It does **not** have to resolve — the spec only requires a URI reference, and `about:blank` is the default meaning "nothing more specific than the status code". But the recommended practice is an HTTPS URL under a domain you control that serves human-readable documentation for that problem type, so a developer who hits an unfamiliar error can just open it. The critical client rule: treat `type` as an **opaque identifier compared for equality**. Never dereference it at request time — that would add a network dependency, a latency cost and an SSRF surface on your error path. Resolution is for humans reading docs. Once published, a type URI's meaning is frozen. Use stable, readable paths like `https://api.example.com/problems/insufficient-funds`, and avoid embedding a version you'll be tempted to bump.
code
json · 7 lines{
"type": "https://api.example.com/problems/insufficient-funds",
"title": "Insufficient funds",
"status": 403,
"detail": "Balance is 30 credits; 50 required.",
"instance": "/transfers/98765"
}go deeper
Say type is a URI naming the kind of problem and that clients compare it as a string.
Add that it need not resolve, that about:blank is the default, and why a URI gives global namespacing.
Cover the no-runtime-dereference rule with its SSRF and availability rationale, plus taxonomy granularity and immutability.
Discuss the type catalogue as a governed, cross-service contract: who mints URIs, how granularity maps to client behaviour, and when the IANA registry applies.
## Why a URI at all An error identifier needs to be unique and stable. A short token like `CONFLICT` is neither: it collides across APIs, and if a client aggregates errors from several services it cannot tell whose conflict it is. RFC 9457 uses a **URI reference** because URIs come with a built-in namespacing mechanism — the authority component — so `https://payments.example.com/problems/conflict` and `https://shipping.example.com/problems/conflict` are unambiguously different. The URI is the stable machine key. `title` and `detail` may be reworded or localized freely; `type` may not change meaning after publication. ## Must it resolve? No. The specification requires a URI reference, not a dereferenceable URL. `urn:` values and non-resolving `https://` paths are both legal, and when `type` is absent it defaults to `"about:blank"`, which carries the meaning "this problem has no semantics beyond the HTTP status code" (used for plain 404s or 500s where nothing more specific applies). That said, the specification *recommends* that it resolve to human-readable documentation, and this is genuinely useful: an integrator hitting `https://api.example.com/problems/insufficient-funds` for the first time can paste it into a browser and learn what causes it and how to fix it. That turns the error body into self-service documentation. If you choose a resolving URL, actually serve a page there — a 404 on your own problem-type URI is a bad look and trains people to ignore the member. ## The hard client rule: don't dereference at runtime Clients must compare `type` as an opaque string. Fetching it during error handling is wrong for several reasons: - **Latency and coupling** on your failure path, precisely when the system is already degraded. - **Availability**: your documentation site becoming unreachable would break error handling. - **Security**: automatically fetching a URL supplied in a response body is a request-forgery primitive. If a client is ever pointed at a hostile or compromised server, it can be induced to make requests to arbitrary hosts. Compare with `==`. Nothing else. ## Choosing good URIs - **Use a domain you control.** Never point at someone else's site — you cannot guarantee its content. - **Prefer HTTPS URLs over `urn:`** unless you have a specific registry reason; developers can open the former. - **Make the path readable and specific**: `/problems/insufficient-funds`, not `/problems/e42`. The URI is read by humans in logs. - **Don't version the URI casually.** A type URI is an identity. If the semantics genuinely change, that is a *new* problem type with a new URI, not a bumped version of the old one. - **Match granularity to client action.** Create a distinct type where a client would behave differently. Two failures a client handles identically don't need separate types; two that need different UX do. - **Keep the taxonomy shallow and enumerable.** A hundred fine-grained types nobody documents is worse than fifteen meaningful ones. ## Relationship to the HTTP status Type and status are layered. The status drives generic HTTP behaviour — retryability heuristics, proxy handling, client library defaults. The type drives your application's logic. A single status will carry many types; a given type should map to exactly one status, since a type whose status varies is really two problems. RFC 9457 also establishes an IANA registry for problem types, so widely-reusable problems can have a well-known identifier rather than every API minting its own — useful for cross-cutting concerns rather than for your domain-specific failures. ## Common mistakes Omitting `type` entirely (leaving everything as `about:blank`) throws away the only machine-readable member and reduces the document to prose plus a status code. Equally common is putting a *per-occurrence* value in `type` — an id, a timestamp — which destroys its usefulness as a class identifier. Per-occurrence identity belongs in `instance`.
- Why is dereferencing the type URI at runtime a security concern?It makes the client fetch a URL that was supplied by the responding server, which is a server-side request forgery primitive if the client is ever pointed at a hostile or compromised endpoint. It also puts a network call and an external availability dependency on the error path, exactly when things are already failing. Compare the URI as an opaque string instead.
- How granular should your problem types be?Create a distinct type wherever a client would take a different action or show different UX. Failures a client handles identically can share a type, while failures requiring different remediation must not. Over-splitting produces a taxonomy nobody documents or handles; under-splitting leaves clients unable to distinguish actionable errors.
saying these in an interview costs you the question
- Claiming the type URI must be a resolvable URL
- Writing clients that HTTP-GET the type URI during error handling
- Putting an occurrence-specific value such as a request id in type instead of instance
- Omitting type entirely and relying on title text for branching
- Bumping a version segment in an existing type URI instead of minting a new type when semantics change