Backend Interview: How Would You Standardize HTTP API Errors with RFC 9457?
What the interviewer evaluates
A multi-tenant API emits errors from the gateway, application, and asynchronous jobs, so clients cannot parse failures reliably. Using RFC 9457, design a shared error contract and explain status codes, media type, type URIs, batch validation, retries, redaction, and version compatibility.
Context and constraints
- A request can cross a CDN, API gateway, business service, and queue.
- Clients must distinguish fixable input errors, authorization failures, throttling, and transient faults.
- Details must not expose stacks, keys, tenant-isolation data, or internal hostnames.
- Existing clients parse legacy fields, so migration must remain backward compatible.
Separate HTTP semantics from business detail
The HTTP status expresses the protocol-level result; the Problem Details body explains the cause. Choose 400, 401, 403, 404, 409, 429, and 5xx by their semantics rather than returning 200 for every failure. Use a stable URI in type for machine classification, a presentational title, and request-specific detail.
Define a minimal extensible contract
Core members are type, title, status, detail, and instance. Name extensions explicitly, such as errors for field problems and retryAfter for a client wait hint. Documentation for each type should state its meaning, allowed status codes, and client action; clients must not branch on natural-language titles.
Share an error boundary across layers
Gateway-generated timeout, authentication, and throttling errors use the same media type but must not impersonate an application business type. Services keep internal error codes for telemetry while exposing only approved types. Propagate a correlation ID across services without copying sensitive detail text.
Clarifying questions before answering
- Do clients branch on status,
type, or a legacycode? This determines the migration adapter. - Can one response contain multiple field-validation failures? This determines the
errorsshape and ordering guarantee. - Can the gateway understand business errors, or only transport and create infrastructure errors? This determines type ownership.
30-second answer framework
“I would preserve the correct HTTP status and return application/problem+json with stable type, title, status, detail, and optional instance. Clients branch on status and type, never on prose; the gateway owns only its error types. Validation, retry hints, correlation IDs, and redaction rules become a versioned contract verified through compatibility matrices and the real request path.”
Step-by-step deep dive
Start with an error-type registry. Each type has a URI, public members, allowed statuses, client action, and security level. Validation failures use 400 with field problems under errors; missing identity and insufficient permission remain 401 and 403; optimistic-concurrency conflicts use 409; throttling uses 429 with an actionable wait hint; unknown faults use 500 or 503 and a generic public type.
Keep Content-Type consistent with the representation. An instance value may identify one request for support, but detail must not contain full URLs, SQL, stack traces, or tenant identifiers. Logs keep the internal cause, correlation ID, and security-audit fields; the client receives only policy-filtered content.
For batch validation, define whether multiple issues are allowed, how field paths are written, and the maximum number of entries. Clients ignore unknown extension members. New members are additive; changing the meaning of an existing type requires a new URI. An asynchronous job exposes its failure through the job resource rather than synchronously leaking queue internals.
Model high-quality answer
“I would maintain a type registry and make the gateway, synchronous services, and asynchronous jobs emit RFC 9457-compatible application/problem+json. Status conveys protocol semantics, type conveys a stable category, and detail describes only this request. Field validation uses a bounded errors extension; 429 has a parseable wait hint; 500 and 503 use generic public types while stacks remain in logs. During migration I keep the legacy code, then switch clients through contract tests, compatibility matrices, and redaction audits.”
Common mistakes
- Returning 200 with a business-failure field, causing caches, monitors, and retryers to infer success.
- Branching on
titleordetail, so translations or wording changes break clients. - Letting every service invent
typeURIs, preventing consistent aggregation. - Returning stacks, SQL, internal domains, or full tenant identifiers in
detail. - Turning a gateway timeout into a business error, causing incorrect retries or user guidance.
Failure symptoms and fixes
When a client cannot decide whether to retry, status, type, and retry guidance usually disagree. Build the registry first, map every layer to a small public set, define a safe default for unknown types, and trace the internal cause with a correlation ID.
Production implementation
Centralize serialization in a shared library or edge adapter while preserving status selection at the service boundary. Schema validation limits extension lengths, array counts, and URI formats; redact before serialization rather than trusting only the gateway. Define exponential backoff, jitter, and idempotency conditions separately for 429, 503, and network timeouts to avoid retry storms.
Verification checklist
Contract tests cover every public type's status, media type, required members, and extensions. Integration tests cross the gateway, service, and queue and assert correlation IDs and redaction. Compatibility tests use a legacy client to verify unknown-member tolerance and preserved code. Load tests measure serialization latency, log sampling, and retry amplification under throttling.
Follow-up questions and answers
Why not define one business error code?
A single code cannot express HTTP caching, authentication, throttling, and retry semantics. Status lets generic infrastructure behave correctly; type carries the stable business category. They have different responsibilities.
Must a type URI be reachable?
The specification permits relative or absolute URIs. Choose a stable, documentable form, but do not make the availability of a documentation page a prerequisite for client handling.
How do you prevent the error body from leaking data?
Use an allowlist of public members, length limits, and sensitive-pattern scanning. Map internal exceptions to generic types and retain only a correlation ID for support. Security tests cover cross-tenant queries, authorization failures, and exception paths.
Scoring rubric
- Semantic accuracy: distinguishes HTTP status meaning from Problem Details members.
- Contract design: proposes stable types, extensions, and versioning.
- Boundary awareness: covers gateways, jobs, redaction, and retry storms.
- Implementability: includes a registry, serialization boundary, compatibility matrix, and tests.
- Risk control: avoids leaks and does not treat unknown types as retryable by default.
Compliance check
Confirm that status codes, type fields, redaction, and retry boundaries stay consistent.
Interview answer checklist
Start with status semantics, then explain that type is the machine-stable identifier and detail is not a contract key. Add gateway ownership, validation, retry, redaction, and compatibility evidence.
One-sentence takeaway
Standardize errors by making status express protocol semantics, type express stable classification, and extensions express actionable detail, with security and compatibility tests protecting the boundary.