denoland/deno · error · TypeError

Return value from serve handler must not be an error respons

Error message

Return value from serve handler must not be an error response (like Response.error())

What it means

Response.error() is a spec-defined network-error marker with type 'error' and no transmissible status, headers, or body, so it cannot be served meaningfully. When a handler returns one, serve inspects the internal response type and throws this TypeError; onError then handles it (default: 500).

Source

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

      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);
        if (!ObjectPrototypeIsPrototypeOf(ResponsePrototype, response)) {
          throw new TypeError(
            "Return value from onError handler must be a response or a promise resolving to a response",
          );
        }
        inner = toInnerResponse(response);

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Return a real status instead: new Response('Service Unavailable', { status: 503 })
  2. Or throw an Error from the handler and let onError produce the 500 response
  3. If wrapping fetch(), map failed upstream calls to a synthetic 502/504 Response

Example fix

// before
Deno.serve(async (req) => {
  try {
    return await fetch(req);
  } catch {
    return Response.error(); // cannot be served
  }
});

// after
Deno.serve(async (req) => {
  try {
    return await fetch(req);
  } catch {
    return new Response("Bad Gateway", { status: 502 });
  }
});
Defensive patterns

Strategy: type-guard

Validate before calling

// Replace error-type responses with a servable one before returning
if (response.type === "error") {
  response = new Response("Service Unavailable", { status: 503 });
}
return response;

Type guard

function isServableResponse(v) {
  return v instanceof Response && v.type !== "error";
}

Try / catch

Deno.serve({
  onError: (err) => new Response("Internal Server Error", { status: 500 }),
  handler,
}); // Response.error() rejections land here as a 500

Prevention

When it happens

Trigger: Returning Response.error() from the handler as an error signal; propagating a Response.error() produced by fetch() failure paths back out of a proxy handler.

Common situations: Copy-pasted fetch-wrapper code that returns Response.error() on catch; using Response.error() as a sentinel value in routing code.

Related errors


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