{"record":{"id":"8c8e47a07c51a39a","repo":"denoland/deno","slug":"return-value-from-serve-handler-must-not-be-an-err","errorCode":null,"errorMessage":"Return value from serve handler must not be an error response (like Response.error())","messagePattern":"Return value from serve handler must not be an error response \\(like Response\\.error\\(\\)\\)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/http/00_serve.ts","lineNumber":760,"sourceCode":"      if (!ObjectPrototypeIsPrototypeOf(ResponsePrototype, response)) {\n        throw new TypeError(\n          \"Return value from serve handler must be a response or a promise resolving to a response\",\n        );\n      }\n\n      // The Response prototype check above passes for Response-like objects\n      // (e.g. a subclass that skipped super(), or a Response from a different\n      // realm/polyfill). Those don't carry the internal slot we read from\n      // below, so reject them with a clear error instead of crashing later.\n      inner = getInnerResponse(response);\n      if (inner === undefined) {\n        throw new TypeError(\n          \"Return value from serve handler must be a Response constructed via the Response constructor in this realm\",\n        );\n      }\n\n      if (inner.type === \"error\") {\n        throw new TypeError(\n          \"Return value from serve handler must not be an error response (like Response.error())\",\n        );\n      }\n\n      if (responseBodyUsed(response)) {\n        throw new TypeError(\n          \"The body of the Response returned from the serve handler has already been consumed\",\n        );\n      }\n    } catch (error) {\n      try {\n        response = await onError(error);\n        if (!ObjectPrototypeIsPrototypeOf(ResponsePrototype, response)) {\n          throw new TypeError(\n            \"Return value from onError handler must be a response or a promise resolving to a response\",\n          );\n        }\n        inner = toInnerResponse(response);","sourceCodeStart":742,"sourceCodeEnd":778,"githubUrl":"https://github.com/denoland/deno/blob/abd22074e47c6a5cd14e9e4e84743f084aa5a575/ext/http/00_serve.ts#L742-L778","documentation":"Deno.serve rejects handler return values whose internal type is 'error', which is the state produced by `Response.error()`. Response.error() is a fetch-API marker meaning 'network error, no HTTP response exists' — it has no status, no headers, and nothing to put on the wire, so it cannot be served. The throw goes to onError and becomes a 500 if unhandled.","triggerScenarios":"`return Response.error()` used as an error signal in a handler, code that proxies upstream fetches and forwards the upstream Response object without checking whether the fetch failed (a failed fetch resolves to Response.error() rather than throwing), or generic `catch` blocks returning Response.error().","commonSituations":"Reverse proxies forwarding `await fetch(upstream)` results verbatim, developers coming from client-side fetch where Response.error() is a familiar pattern, error handlers that want to 'signal failure' to the server runtime.","solutions":["Throw an Error from the handler instead; Deno.serve routes it to onError which returns a 500","When proxying fetch, check `upstream.type === 'error'` and map it to a real status: `new Response('Bad Gateway', { status: 502 })`","Configure onError to produce the exact failure response you want"],"exampleFix":"// before\nDeno.serve(async (req) => {\n  const upstream = await fetch(target, req);\n  return upstream; // fetch network failure resolves to Response.error()\n});\n\n// after\nDeno.serve(async (req) => {\n  try {\n    const upstream = await fetch(target, req);\n    if (upstream.type === 'error') throw new Error('upstream unreachable');\n    return upstream;\n  } catch {\n    return new Response('Bad Gateway', { status: 502 });\n  }\n});","handlingStrategy":"validation","validationCode":"// Check the public type before returning a possibly-error Response:\nif (response.type === 'error') {\n  response = new Response('Bad Gateway', { status: 502 });\n}\nreturn response;","typeGuard":"const isErrorResponse = (r: Response): boolean => r.type === 'error';","tryCatchPattern":"// Prefer throwing; onError owns the wire format:\nif (badUpstream) throw new Error('upstream unreachable');\n// onError: (e) => new Response('Internal Server Error', { status: 500 })","preventionTips":["Treat Response.error() as a client-fetch concept; never return it from a server","When proxying fetch, always inspect r.type === 'error' before forwarding","Use onError for failure responses instead of error-marker objects"],"tags":["http","deno-serve","response-error","fetch","proxy"],"backgroundTag":"error-response-returned","analyzedSha":"abd22074e47c6a5cd14e9e4e84743f084aa5a575","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}