denoland/deno · error · TypeError

Return value from serve handler must be a Response…

Error message

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

What it means

After the prototype check passes, Deno.serve reads the internal Deno response slot with getInnerResponse(). A value that looks like a Response (shares the prototype) but lacks that internal slot triggers this TypeError. Typical sources: a subclass that never called super(), or a Response object created by a different realm/polyfill (undici, node-fetch, a mock) that merely mimics the prototype chain.

Solutions

  1. Always call `super(init)` in Response subclasses before setting extra fields
  2. Do not import or return Response instances from undici/node-fetch/polyfills; use the global Response
  3. Re-wrap foreign instances: `return new Response(foreign.body, foreign)`
  4. In tests, build stubs with the real constructor instead of Object.create(Response.prototype)

Example fix

// before
class TeeShirt extends Response {
  constructor(size) {
    this.size = size; // no super() -> no internal slot
  }
}

// after
class TeeShirt extends Response {
  size;
  constructor(size) {
    super(`size=${size}`); // internal slot created
    this.size = size;
  }
}
Defensive patterns

Strategy: validation

Validate before calling

// Re-wrap anything that might not carry the Deno internal slot:
let out = await callback(req, info);
if (!(out instanceof Response) || Object.getPrototypeOf(out) === null) {
  out = new Response(out.body, out);
}
return out;

Try / catch

// The internal slot is not observable from JS; rely on onError to convert this to a 500
Deno.serve({ handler, onError: () => new Response('Internal Server Error', { status: 500 }) });

Prevention

When it happens

Trigger: `class MyResponse extends Response` whose constructor skips `super(...)` and only sets fields, returning a Response produced by an npm dependency that ships its own fetch implementation, or a stub object created with `Object.create(Response.prototype)` in tests.

Common situations: Custom Response subclasses adding metadata fields, framework middlewares that wrap or replace the Response with a polyfilled one, test doubles leaking into a running server, bundlers aliasing Response to a polyfill.

Related errors


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

Appendix: source

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

          request,
          new ServeHandlerInfo(innerRequest),
        );
      }

      // Throwing Error if the handler return value is not a Response class
      if (!ObjectPrototypeIsPrototypeOf(ResponsePrototype, response)) {
        throw new TypeError(
          "Return value from serve handler must be a response or a promise resolving to a response",
        );
      }

      // The Response prototype check above passes for Response-like objects
      // (e.g. a subclass that skipped super(), or a Response from a different
      // realm/polyfill). Those don't carry the internal slot we read from
      // below, so reject them with a clear error instead of crashing later.
      inner = getInnerResponse(response);
      if (inner === undefined) {
        throw new TypeError(
          "Return value from serve handler must be a Response constructed via the Response constructor in this realm",
        );
      }

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

View on GitHub (pinned to abd22074e4)