denoland/deno · error · TypeError
Return value from onError handler must be a Response constru
Error message
Return value from onError handler must be a Response constructed via the Response constructor in this realm
What it means
The Response returned by onError goes through the same validation as a handler response: it must be a real Response carrying the internal slot of this realm. toInnerResponse returns undefined for cross-realm or polyfill instances (and prototype-forged objects), and serve rejects them with this TypeError. Because it is thrown inside the error path, only the internal 'Exception in onError while handling exception' log remains; the client gets a 500.
Source
Thrown at ext/http/00_serve.ts:761
);
}
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,
);
response = internalServerError();
inner = toInnerResponse(response);
}
}
if (span) {View on GitHub (pinned to 89f33cbef2)
Solutions
- Re-create the response inside onError with the built-in Response constructor
- Buffer cross-realm bodies first: new Response(await foreign.text(), { status: 500, headers: foreign.headers })
- Ensure any Response subclass used in onError calls super(...)
Example fix
// before
import { Response as NodeResponse } from "npm:undici";
Deno.serve({
onError: (e) => new NodeResponse(e.message, { status: 500 }), // wrong realm
handler,
});
// after
import { Response as NodeResponse } from "npm:undici";
Deno.serve({
onError: (e) => new Response(e.message, { status: 500 }), // built-in Response
handler,
}); Defensive patterns
Strategy: type-guard
Validate before calling
// Normalize onError output to this realm's Response
const onError = (err) => {
const r = buildErrorResponse(err); // may be cross-realm
return r instanceof Response && r.constructor === Response
? r
: new Response("Internal Server Error", { status: 500 });
}; Type guard
function isRealmResponse(v) {
return v instanceof Response && v.constructor === Response;
} Try / catch
// A failing onError already degrades to a logged 500; keep it simple:
const onError = (err) => new Response("Internal Server Error", { status: 500 }); Prevention
- Construct error responses inline with the built-in Response constructor
- Do not return undici/Worker Responses from onError
- Unit-test the onError path, not just the happy path
When it happens
Trigger: onError returning a Response imported from an npm package (e.g. undici), a Response built inside a Worker, or a subclass that skipped super().
Common situations: Centralized error handlers that re-export or forward Response objects from third-party SDKs; polyfilled environments layered on top of Deno.
Related errors
- Return value from serve handler must be a Response construct
- Return value from onError handler must be a response or a pr
- Already upgraded
- Already closed
- Request closed
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/e73e78f6d6c228f9.
Report an issue: GitHub.