denoland/deno · error · TypeError

Return value from serve handler must be a response or a…

Error message

Return value from serve handler must be a response or a promise resolving to a response

What it means

Deno.serve validates that the handler's settled return value is a real Response before sending anything. This TypeError fires when the value is not even on the Response prototype chain — a plain object, string, undefined, or null. The throw is caught by the surrounding try/catch and passed to onError, which defaults to a 500 response.

Solutions

  1. Make every code path return a Response: use `new Response(...)`, `Response.json(...)`, or `Response.redirect(...)`
  2. Add a fallback at the end of the handler: `return response ?? new Response('Not Found', { status: 404 })`
  3. Type the handler as `(req: Request) => Response | Promise<Response>` so the compiler flags missing returns
  4. Let thrown errors go to onError instead of returning error-shaped plain objects

Example fix

// before
Deno.serve(async (req) => {
  if (!req.headers.get('authorization')) return; // undefined
  return { status: 200, body: 'ok' }; // plain object
});

// after
Deno.serve(async (req) => {
  if (!req.headers.get('authorization')) {
    return new Response('Unauthorized', { status: 401 });
  }
  return Response.json({ ok: true });
});
Defensive patterns

Strategy: type-guard

Validate before calling

// Guarantee every path yields a Response before it reaches Deno.serve:
const handler = async (req: Request): Promise<Response> => {
  const data = await compute(req);
  return Response.json(data ?? null); // no path returns undefined
};

Type guard

const isResponse = (v: unknown): v is Response => v instanceof Response;

Try / catch

// Centralized safety net via onError (receives this TypeError as-is):
Deno.serve({
  handler,
  onError: (e) => {
    console.error(e);
    return new Response('Internal Server Error', { status: 500 });
  },
});

Prevention

When it happens

Trigger: An async handler with a code path that has no return statement (resolves undefined), returning raw JSON data (`return { ok: true }`), returning a string body directly, or forgetting to return the Response from an inner helper (`await next()` style middleware that drops the value).

Common situations: Porting Express-style handlers that returned plain objects or used res.json(), early-return guards (`if (!auth) return;`), refactors that renamed the response variable but left `return;` behind.

Related errors


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

Appendix: source

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

        response = await callback();
      } else {
        innerRequest = new InnerRequest(req, context);
        const request = fromInnerRequest(innerRequest, "immutable");
        innerRequest.request = request;

        if (span) {
          updateSpanFromRequest(span, request);
        }

        response = await callback(
          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())",

View on GitHub (pinned to abd22074e4)