{"record":{"id":"b41ecb8ad95ce0ec","repo":"denoland/deno","slug":"return-value-from-serve-handler-must-be-a-response","errorCode":null,"errorMessage":"Return value from serve handler must be a response or a promise resolving to a response","messagePattern":"Return value from serve handler must be a response or a promise resolving to a response","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/http/00_serve.ts","lineNumber":743,"sourceCode":"        response = await callback();\n      } else {\n        innerRequest = new InnerRequest(req, context);\n        const request = fromInnerRequest(innerRequest, \"immutable\");\n        innerRequest.request = request;\n\n        if (span) {\n          updateSpanFromRequest(span, request);\n        }\n\n        response = await callback(\n          request,\n          new ServeHandlerInfo(innerRequest),\n        );\n      }\n\n      // Throwing Error if the handler return value is not a Response class\n      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())\",","sourceCodeStart":725,"sourceCodeEnd":761,"githubUrl":"https://github.com/denoland/deno/blob/abd22074e47c6a5cd14e9e4e84743f084aa5a575/ext/http/00_serve.ts#L725-L761","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Make every code path return a Response: use `new Response(...)`, `Response.json(...)`, or `Response.redirect(...)`","Add a fallback at the end of the handler: `return response ?? new Response('Not Found', { status: 404 })`","Type the handler as `(req: Request) => Response | Promise<Response>` so the compiler flags missing returns","Let thrown errors go to onError instead of returning error-shaped plain objects"],"exampleFix":"// before\nDeno.serve(async (req) => {\n  if (!req.headers.get('authorization')) return; // undefined\n  return { status: 200, body: 'ok' }; // plain object\n});\n\n// after\nDeno.serve(async (req) => {\n  if (!req.headers.get('authorization')) {\n    return new Response('Unauthorized', { status: 401 });\n  }\n  return Response.json({ ok: true });\n});","handlingStrategy":"type-guard","validationCode":"// Guarantee every path yields a Response before it reaches Deno.serve:\nconst handler = async (req: Request): Promise<Response> => {\n  const data = await compute(req);\n  return Response.json(data ?? null); // no path returns undefined\n};","typeGuard":"const isResponse = (v: unknown): v is Response => v instanceof Response;","tryCatchPattern":"// Centralized safety net via onError (receives this TypeError as-is):\nDeno.serve({\n  handler,\n  onError: (e) => {\n    console.error(e);\n    return new Response('Internal Server Error', { status: 500 });\n  },\n});","preventionTips":["Type handlers as (req: Request) => Response | Promise<Response> so TS flags missing returns","Enable noImplicitReturns to catch branches that fall through","End handlers with an explicit fallback: return response ?? new Response('Not Found', { status: 404 })"],"tags":["http","deno-serve","handler-contract","validation"],"backgroundTag":"serve-handler-invalid-return","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"}