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

  1. Pass the handler: Deno.serve(handler) or Deno.serve({ port: 8000, handler })
  2. If options come from a config file, merge them with a local handler before calling serve
  3. 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

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


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