API & webhooks
Investigate REST API 500 with Safe Error Boundaries and Evidence
Trace a REST API 500 using a controlled reproduction, request identifiers, server logs, and bounded client behavior without exposing internal exceptions.
Treat the status as a symptom to investigate
A 500 Internal Server Error reports that the server encountered an unexpected condition while handling the request. It does not, by itself, identify the failing database query, missing configuration value or application defect. Begin with the smallest authorized request that reproduces the failure and preserve its status, timing and safe diagnostic context.
The HTTP 500 reference describes this broad server-error category. A client-side JSON parsing exception is different: it can occur while reading a 200 HTML page, a 500 HTML error page or malformed content. Record the HTTP response before replacing it with a parsing failure.
Separate the public error from internal evidence
async function withErrorBoundary(operation, logFailure, requestId) {
try {
return await operation();
} catch (error) {
logFailure({requestId, error});
return Response.json({
type: "about:blank",
title: "Internal Server Error",
status: 500,
requestId
}, {
status: 500,
headers: {
"Content-Type": "application/problem+json",
"Cache-Control": "no-store"
}
});
}
}
The operation supplies the successful Response. If it throws or returns a rejected promise, the wrapper logs a correlation identifier and returns a generic public problem response. This makes a synchronous throw and an asynchronous rejection follow the same controlled path instead of leaving one unhandled.
The caller must provide a logger appropriate to its environment and a validated, non-secret request identifier. This small example assumes the logger does not throw. A production logging strategy also needs redaction, access controls and an intentional failure policy; copying the example does not create a complete observability system.
Use the identifier to join the right records
Look up the corresponding request in application and infrastructure logs. Compare its timestamp, deployment version and route with a known-good request. A new release that fails only on one code path suggests a different investigation from a database outage affecting every endpoint.
Inspect the first relevant failure rather than only the final error generated while handling it. A serialization exception may follow a failed query, and an error page can itself fail to render. Preserve enough causal context to identify the original failure without exposing it in the public response.
Do not return stack traces, connection strings or complete internal exception messages to clients. The public body can provide a stable problem title and a safe identifier. The Problem Details specification defines a structured error representation, but it does not require publishing private debugging information.
Choose retries according to the operation
A 500 does not prove that a write had no effect. A service might create a record and then fail while building the response. Blindly retrying a purchase, transfer or invitation can duplicate work unless the API provides an appropriate idempotency mechanism and the client follows that contract.
For a read-only request, a limited retry may be appropriate when the failure is transient, but use a bounded count and delay. Record the original error, respect the provider's guidance and avoid a rapid loop that adds load to an already failing service. Authentication and malformed-input failures usually require a different response.
Exercise the error path with a controlled failure
Test the wrapper with a successful response, a thrown exception and a rejected asynchronous operation. Assert that the public status and body agree, the logger receives the matching identifier and the internal exception text is absent from the public JSON. Also check that the successful path is returned without being rewrapped as an error.
Use the REST API Testing Tool to inspect a controlled test endpoint's status, body and exposed response headers. A browser network rejection may conceal a server response because of CORS; confirm that separately before treating it as evidence that the service returned no HTTP status.
After fixing the underlying cause, repeat both the previously failing request and the neighboring success case. Keep a targeted regression for the triggering condition. Changing every server exception to 200 hides the defect from monitoring and forces each client to invent a separate error-detection rule.


