denoland/deno · error · TypeError

Return value from onError handler must be a Response…

Error message

Return value from onError handler must be a Response constructed via the Response constructor in this realm

What it means

The onError path performs the same two-step validation as the main handler: first the prototype check, then toInnerResponse()/getInnerResponse() for the internal Deno slot. This TypeError means the Response returned by onError passed `instanceof Response` but carries no internal slot — it came from a subclass that skipped super(), or from another realm/polyfill (undici, node-fetch, a mock). Deno then logs 'Exception in onError while handling exception' and sends the built-in 500.

Solutions

  1. Build onError's return value with the global Response constructor
  2. Call `super(init)` in any Response subclass used for error replies
  3. Re-wrap foreign objects: `return new Response(foreign.body, foreign)`

Example fix

// before
import { Response as NodeFetchResponse } from 'node-fetch';
onError: (e) => new NodeFetchResponse(e.message, { status: 500 }),

// after
onError: (e) => new Response(e.message, { status: 500 }), // global Response
Defensive patterns

Strategy: validation

Validate before calling

// Normalize anything onError might return into a native Response:
onError: (e) => {
  const out = buildErrorPage(e); // possibly foreign
  return out instanceof Response ? new Response(out.body, out) : new Response('Internal Server Error', { status: 500 });
}

Try / catch

// A second failure falls back to the built-in 500 automatically; log it:
// 'Exception in onError while handling exception' appears in server logs — check there first
Deno.serve({ handler, onError });

Prevention

When it happens

Trigger: onError returning a Response imported from undici/node-fetch, a Response subclass in the error path whose constructor never calls super(), or `Object.create(Response.prototype)` stubs used to fake error responses.

Common situations: Centralized error handlers in frameworks that import their own fetch polyfill, custom error-Response wrapper classes, test harnesses injecting fake Responses into onError.

Related errors


AI-assisted analysis of denoland/deno@abd22074e4 (2026-08-20). Data as JSON: /api/errors/e73e78f6d6c228f9. Report an issue: GitHub.

Appendix: source

Thrown at ext/http/00_serve.ts:780

        );
      }

      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 abd22074e4)