denoland/deno · error · TypeError

First argument to 'respondWith' must be a Response…

Error message

First argument to 'respondWith' must be a Response constructed via the Response constructor in this realm

What it means

A stricter follow-up check after the prototype test in `respondWith`: `toInnerResponse(resp)` must find Deno's internal response slot. Objects that merely imitate a Response — `Object.create(Response.prototype)`, a polyfilled Response (undici/node-fetch/cross-fetch), or a Response created in another realm — pass the prototype check but carry no internal slot, and would crash later when the internal body is read. Deno rejects them up front (mirrors the `Deno.serve` guard from PR #34416).

Solutions

  1. Rebuild the response with the ambient global constructor: `new Response(poly.body, poly)`
  2. Configure imports/bundlers so Deno's global `Response` is used instead of a polyfill
  3. In tests, stub with real `Response` instances rather than prototype fakes

Example fix

// before
const resp = new UndiciResponse("hi");
await e.respondWith(resp);

// after
const poly = new UndiciResponse("hi");
await e.respondWith(new Response(poly.body, poly));
Defensive patterns

Strategy: type-guard

Validate before calling

let resp = await maybePromise;
if (!(resp instanceof Response)) {
  // rebuild with the ambient constructor so it carries the internal slot
  resp = new Response((resp as Response | null)?.body ?? null, resp as ResponseInit);
}
await e.respondWith(resp);

Type guard

const isRealmResponse = (v: unknown): v is Response =>
  v instanceof Response; // same-realm check; polyfills fail this only if patched — prefer toInnerResponse semantics: rebuild when in doubt

Try / catch

try {
  await e.respondWith(resp);
} catch (err) {
  if (err instanceof TypeError && err.message.includes("this realm")) {
    await e.respondWith(new Response(resp.body, resp)); // second attempt with global Response
  } else throw err;
}

Prevention

When it happens

Trigger: Passing `new undici.Response(...)` or a node-fetch Response to `respondWith`; handing over `Object.create(Response.prototype)` test doubles; Responses crossing worker/vm realm boundaries.

Common situations: Libraries that bundle their own fetch implementation (SSR frameworks, SDKs, isomorphic-fetch/cross-fetch); mocking Response in tests; code that runs the handler inside a separate worker or vm context.

Related errors


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

Appendix: source

Thrown at ext/http/01_http.js:213

  writeStreamRid,
) {
  return async function respondWith(resp) {
    try {
      resp = await resp;
      if (!(ObjectPrototypeIsPrototypeOf(ResponsePrototype, resp))) {
        throw new TypeError(
          "First argument to 'respondWith' must be a Response or a promise resolving to a Response",
        );
      }

      // The Response prototype check above passes for Response-like objects
      // that don't carry the internal slot (e.g. `Object.create(Response.prototype)`
      // or a polyfilled/foreign-realm Response). Reject those here instead of
      // crashing later on `innerResp.body`. Mirrors the Deno.serve guard
      // added in #34416.
      const innerResp = toInnerResponse(resp);
      if (innerResp === undefined) {
        throw new TypeError(
          "First argument to 'respondWith' must be a Response constructed via the Response constructor in this realm",
        );
      }

      // If response body length is known, it will be sent synchronously in a
      // single op, in other case a "response body" resource will be created and
      // we'll be streaming it.
      /** @type {ReadableStream<Uint8Array> | Uint8Array | null} */
      let respBody = null;
      if (innerResp.body !== null) {
        if (innerResp.body.unusable()) {
          throw new TypeError("Body is unusable");
        }
        if (
          ObjectPrototypeIsPrototypeOf(
            ReadableStreamPrototype,
            innerResp.body.streamOrStatic,
          )

View on GitHub (pinned to 9ad36f7a2c)