denoland/deno · error · TypeError
Return value from onError handler must be a response or a pr
Error message
Return value from onError handler must be a response or a promise resolving to a response
What it means
When a serve handler throws, Deno calls the configured onError callback and expects it (or its promise) to resolve to a Response to send instead. If onError returns a non-Response, serve throws this TypeError; the fallback then logs 'Exception in onError while handling exception' and closes the request with a 500.
Source
Thrown at ext/http/00_serve.ts:755
);
}
if (inner.type === "error") {
throw new TypeError(
"Return value from serve handler must not be an error response (like Response.error())",
);
}
if (responseBodyUsed(response)) {
throw new TypeError(
"The body of the Response returned from the serve handler has already been consumed",
);
}
} catch (error) {
try {
response = await onError(error);
if (!ObjectPrototypeIsPrototypeOf(ResponsePrototype, response)) {
throw new TypeError(
"Return value from onError handler must be a response or a promise resolving to a response",
);
}
inner = toInnerResponse(response);
if (inner === undefined) {
throw new TypeError(
"Return value from onError handler must be a Response constructed via the Response constructor in this realm",
);
}
} catch (error) {
if (otelState.METRICS_ENABLED) {
op_http_metric_handle_otel_error(req);
}
internals.log(
"error",
"Exception in onError while handling exception",
error,
);View on GitHub (pinned to 89f33cbef2)
Solutions
- Make onError return a Response: new Response(message, { status: 500 })
- Cover every branch of onError, including its own catch/finally paths
- Keep using throw inside handlers; reserve onError for shaping the error page
Example fix
// before
Deno.serve({
onError: (e) => `Error: ${e.message}`, // string, not a Response
handler,
});
// after
Deno.serve({
onError: (e) => new Response(`Error: ${e.message}`, { status: 500 }),
handler,
}); Defensive patterns
Strategy: type-guard
Validate before calling
// Ensure onError always yields a Response
const safeOnError = async (err) => {
const r = await onError?.(err);
return r instanceof Response ? r : new Response("Internal Server Error", { status: 500 });
}; Type guard
function isValidOnErrorResult(v) {
return v instanceof Response;
} Try / catch
// serve already wraps a failing onError with a 500 and logs
// 'Exception in onError while handling exception'; keep onError trivial and total:
const onError = (err) => new Response(`Error: ${err.message}`, { status: 500 }); Prevention
- Keep onError synchronous and always returning new Response(...)
- Do not forward third-party Response types out of onError
- Return a plain 500 on any unexpected shape instead of the raw value
When it happens
Trigger: onError returning a string like `Error: ${message}`; onError that forgets a return on some branch; onError returning the caught error object itself; onError that returns undefined after logging.
Common situations: Converting an Express-style error middleware that replied with res.send(text); adding logging-first onError handlers without a return value.
Related errors
- Return value from serve handler must be a response or a prom
- Return value from serve handler must not be an error respons
- Return value from onError handler must be a Response constru
- Already upgraded
- Already closed
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/04bde810e2a261ae.
Report an issue: GitHub.