API Errors
Well-designed API errors are as important as the successful responses your API returns. A good error response tells the caller what went wrong, whether the problem is on their side or yours, and what they can do about it. Zuplo encourages every API to return standard, actionable error messages so that developers integrating with your API spend less time guessing and more time building.
This page explains the error format Zuplo uses by default, how it shows up in the gateway, and how to customize the shape of error responses when your API has its own conventions.
Why standard errors matter
When every endpoint invents its own error shape, client code becomes brittle. Developers have to special-case each response, parse ad-hoc fields, and guess at whether a failure is retryable. Standardizing errors across your API produces three concrete benefits:
- Faster integration -- consumers write one error handler that works everywhere.
- Better observability -- logs, dashboards, and tools can parse errors consistently.
- Clearer contracts -- your OpenAPI document can describe errors using the same schema for every operation.
A good error response is short, machine-readable, and specific. It identifies the kind of problem, says what happened in human terms, and includes enough context (a request ID, a field name, a retry hint) that the caller can take the next step without opening a support ticket.
The Problem Details format
Zuplo defaults to the Problem Details for HTTP APIs
format defined by RFC 7807.
Problem Details is a small, widely adopted JSON schema for representing errors
from HTTP APIs. Responses use the application/problem+json content type and
follow a consistent shape.
A typical Problem Details response from Zuplo looks like this:
Code
The standard fields are:
type-- a URI that identifies the kind of problem. Every occurrence of the same problem should share the sametype.title-- a short, human-readable summary that should stay consistent for a giventype.status-- the HTTP status code, duplicated in the body so clients that log only the payload still see it.detail-- a human-readable explanation of this particular occurrence. This is the field that varies from request to request.instance-- a URI or path that identifies the specific request that produced the error.
Problem Details also allows extensions -- arbitrary additional fields that
carry problem-specific data. Zuplo uses extensions to include a trace object
containing the request ID, build ID, and timestamp on every error, which makes
support requests easy to correlate with logs.
Keep title stable for a given error type and put request-specific information
in detail or in extensions. Clients match on type and title; humans read
detail.
How Zuplo uses Problem Details
Zuplo's built-in policies, handlers, and system errors all return Problem
Details responses out of the box. When an inbound policy rejects a request --
for example, the
API Key Authentication policy when a key is
missing, or the Rate Limiting policy when
a caller exceeds their quota -- the response body is a Problem Details object
with a type, title, status, and trace. The same is true for system
responses like unmatched routes and unsupported HTTP methods.
This means that consumers of a Zuplo-fronted API get a consistent error contract for free across gateway errors, even before you write any custom code.
Returning Problem Details from custom code
When you write a custom handler or a
custom policy, return Problem Details responses
using the HttpProblems helper from @zuplo/runtime. The helper has a method
for every HTTP status code and automatically fills in type, title, status,
instance, and trace.
Code
For the full list of methods and options, see the HttpProblems helper reference.
Adding context with detail and extensions
Override the default fields when you have something more useful to say. Use
detail for a human-readable explanation of the specific failure, and use
extension members for structured data that clients can act on.
Code
The title stays consistent for every instance of this error, while detail
and the parameter extension tell the caller exactly what to fix.
Throwing runtime errors
If your code throws rather than returning a response, use RuntimeError and
ConfigurationError to attach structured context that the gateway can surface.
Thrown errors are converted to Problem Details responses automatically, and any
extensionMembers you attach flow through to the response body.
Code
See Runtime Errors for details on both error classes and patterns for mapping them to problem responses.
Customizing the error response format
Problem Details is the default, but it isn't the only option. If your API already has an established error schema, or if you want to wrap every error in a custom envelope, Zuplo provides two levels of customization.
Per-response overrides
The simplest customization is to override fields on individual responses.
HttpProblems lets you change title, detail, type, instance, and add
arbitrary extensions without giving up the standard format.
Code
This approach keeps the Problem Details shape while letting you customize types, link to documentation, and include plan-specific or caller-specific metadata.
Formatting every error with ProblemResponseFormatter
For more control, use the
ProblemResponseFormatter
to build problem responses directly. This is useful when you want to compute the
problem body yourself -- for example, mapping an upstream error payload into
your own error taxonomy.
Code
Replacing the error format globally
To change the shape of every error response the gateway returns -- including errors raised by built-in policies -- register a global error handler in your runtime extensions. The handler runs for any unhandled error in the pipeline and returns the response of your choice.
Code
Global error handlers let you keep Problem Details internally while projecting a different schema to your callers, or replace the format entirely if your API has an established error contract.
Replacing the default format means you also take responsibility for including trace information, preserving status codes, and documenting the new schema in your OpenAPI document. Most APIs are best served by customizing Problem Details fields rather than replacing the format.
Customizing specific system errors
Some gateway behaviors have dedicated extension points for error customization. For example, you can replace the default 404 response by registering a not-found handler, which is useful for serving custom error pages or matching your API's error schema on unmatched routes.
Choosing an approach
| Scenario | Approach |
|---|---|
| Return a one-off error from a handler or policy | HttpProblems with detail and extensions |
| Build problem responses from external error payloads | ProblemResponseFormatter.format() |
| Attach context to thrown errors | RuntimeError with extensionMembers |
| Replace the error schema for every response | Global error handler via runtime.addErrorHandler |
| Customize only the 404 response | Not-found handler |
Related resources
- HttpProblems helper
- ProblemResponseFormatter
- Runtime Errors
- Not-found Handler
- Runtime Extensions
- Custom Handlers
- Policies
- RFC 7807: Problem Details for HTTP APIs