denoland/deno · error · TypeError

First argument to 'respondWith' must be a Response or a prom

Error message

First argument to 'respondWith' must be a Response or a promise resolving to a Response

What it means

Thrown inside the async respondWith closure created per-request by Deno.serveHttp(httpConn). It awaits the value the handler passed to httpConn.respondWith() and then requires it to have Response.prototype in its prototype chain. Anything that does not resolve to a real Response instance (plain object, string, number, undefined, a Response-like duck-typed object) triggers this TypeError.

Source

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

        const reqEvt = await httpConn.nextRequest();
        // Change with caution, current form avoids a v8 deopt
        return { value: reqEvt ?? undefined, done: reqEvt === null };
      },
    };
  }
}

function createRespondWith(
  httpConn,
  request,
  readStreamRid,
  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

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Always resolve to a real Response: httpConn.respondWith(new Response(body, { status, headers })).
  2. Audit every code path in the handler passed to respondWith so each returns a Response (add a fallback `return new Response('error', { status: 500 })`).
  3. If you have a plain object, construct a Response from it instead of passing it directly.
  4. Use Deno.serve(handler) instead of the low-level serveHttp loop unless you need raw connection control.

Example fix

// before
httpConn.respondWith({ status: 200, body: "hello" });

// after
httpConn.respondWith(new Response("hello", { status: 200 }));
Defensive patterns

Strategy: type-guard

Type guard

async function isResponse(v: unknown): Promise<boolean> { return Object.prototype.isPrototypeOf.call(Response.prototype, await v); }

Try / catch

try { await httpConn.respondWith(handlerResult); } catch (e) { if (e instanceof TypeError && /respondWith/.test(e.message)) { await httpConn.respondWith(new Response("bad handler", { status: 500 })); return; } throw e; }

Prevention

When it happens

Trigger: Calling httpConn.respondWith(new Request(...)) instead of a Response; returning/resolving to a plain object { status: 200, body: 'x' }; passing a string promise; passing undefined because an if/else branch forgot a return; passing a fetch-style WHATWG Response polyfill.

Common situations: Hand-rolled Deno.serveHttp loops migrated from service-worker style code; async handlers where one branch returns nothing; JSON helpers that return parsed bodies rather than Response objects; third-party Response polyfills leaking into the handler.

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/43b40eec402e4d12. Report an issue: GitHub.