denoland/deno · error · TypeError
Cannot serve HTTP requests: either a `handler` or `options`
Error message
Cannot serve HTTP requests: either a `handler` or `options` must be specified
What it means
Deno.serve supports three call shapes: serve(handler), serve(options, handler), and serve(options-with-handler). The argument parser assigns handler only from a function argument or options.handler; when neither is present it throws this TypeError at startup so the misconfiguration fails fast instead of binding a useless listener.
Source
Thrown at ext/http/00_serve.ts:1150
// Flag to track if DENO_SERVE_ADDRESS override has been consumed
let serveAddressOverrideConsumed = false;
function serve(arg1, arg2) {
let options: RawServeOptions | undefined;
let handler: RawHandler | undefined;
if (typeof arg1 === "function") {
handler = arg1;
} else if (typeof arg2 === "function") {
handler = arg2;
options = arg1;
} else {
options = arg1;
}
if (handler === undefined) {
if (options === undefined) {
throw new TypeError(
"Cannot serve HTTP requests: either a `handler` or `options` must be specified",
);
}
handler = options.handler;
}
if (typeof handler !== "function") {
throw new TypeError(
`Cannot serve HTTP requests: handler must be a function, received ${typeof handler}`,
);
}
if (options === undefined) {
options = { __proto__: null };
}
if (serveAddressOverrideConsumed) {
return serveInner(options, handler);
}
View on GitHub (pinned to 89f33cbef2)
Solutions
- Pass the handler: Deno.serve(handler) or Deno.serve({ port: 8000, handler })
- If options come from a config file, merge them with a local handler before calling serve
- Check `typeof handler === 'function'` before calling serve in dynamic setups
Example fix
// before
Deno.serve({ port: 8000 }); // no handler
// after
Deno.serve({ port: 8000, handler: (req) => new Response("ok") }); Defensive patterns
Strategy: validation
Validate before calling
// Validate serve arguments before calling it
function assertServeArgs(arg1, arg2) {
const fn = typeof arg1 === "function" ? arg1 : (typeof arg2 === "function" ? arg2 : arg1?.handler);
if (typeof fn !== "function") {
throw new TypeError("Deno.serve requires a handler function");
}
} Type guard
function hasServeHandler(arg1, arg2) {
return typeof arg1 === "function" || typeof arg2 === "function" ||
(typeof arg1?.handler === "function");
} Prevention
- Default the handler when building options from config: serve({ ...cfg, handler: cfg.handler ?? fallback })
- Run a smoke start of the server in CI so missing-handler configs fail there
- Prefer the explicit serve({ port, handler }) shape in examples
When it happens
Trigger: Calling Deno.serve() with no arguments; passing only network options such as Deno.serve({ port: 8000 }) and forgetting handler; passing the handler as the second argument to a plain options object that already lacks it.
Common situations: Migrating from std/http serve or Express, where the wiring differs; splitting config into a shared object and forgetting to add the handler key when copying examples.
Related errors
- Cannot serve HTTP requests: handler must be a function, rece
- Already upgraded
- Already closed
- Request closed
- Invalid response
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/7cc789d83f2b6729.
Report an issue: GitHub.