denoland/deno · error · TypeError
First argument to 'respondWith' must be a Response or a…
Error message
First argument to 'respondWith' must be a Response or a promise resolving to a Response
What it means
`Deno.HttpConn.respondWith()` (the low-level API obtained from `Deno.serveHttp(conn)`) accepts only a `Response` instance or a promise resolving to one. Before writing to the socket, Deno awaits the value and verifies it is on the `Response.prototype` chain; anything else — a string, plain object, `undefined`, a `Request` — throws this TypeError and the request fails.
Solutions
- Wrap the payload: `respondWith(new Response(body, { status, headers }))`
- Check async handlers actually `return` the Response
- If you return a promise, make sure it resolves to a real `Response`
Example fix
// before
await requestEvent.respondWith(JSON.stringify(data));
// after
await requestEvent.respondWith(
new Response(JSON.stringify(data), { headers: { "content-type": "application/json" } }),
); Defensive patterns
Strategy: type-guard
Validate before calling
const resp = await maybePromise;
if (!(resp instanceof Response)) {
throw new TypeError("handler must return a Response");
}
await e.respondWith(resp); Type guard
const isResponse = (v: unknown): v is Response => v instanceof Response;
Try / catch
try {
await e.respondWith(handler(requestEvent.request));
} catch (err) {
if (err instanceof TypeError && err.message.includes("respondWith")) {
await e.respondWith(new Response("handler error", { status: 500 }));
} else throw err;
} Prevention
- Always construct the reply with `new Response(...)` — never return raw strings/objects
- Type handlers as `(req: Request) => Response | Promise<Response>` so the compiler catches it
- Check that async handlers `return` the Response, not just build it
When it happens
Trigger: `requestEvent.respondWith("hello")`; `respondWith({ status: 200 })`; an async handler that forgets `return` before `new Response(...)` so the awaited promise resolves to `undefined`; passing a `Request` back by mistake.
Common situations: Migrating from service-worker fetch-event or Express-style `(req, res)` handlers; refactors that drop the return statement; responding with serialized JSON instead of a Response object.
Related errors
- Body is unusable
- ${error}
- First argument to 'respondWith' must be a Response…
- ReadableStream is locked
- Already closed
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/43b40eec402e4d12.
Report an issue: GitHub.
Appendix: 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 andView on GitHub (pinned to 9ad36f7a2c)