denoland/deno · error · TypeError
Invalid type for fetch: must be a function
Error message
Invalid type for fetch: must be a function
What it means
Thrown by Deno's declarative server registration (registerDeclarativeServer), which runs when a module exports a top-level 'fetch' export (the module-as-server style used by 'deno serve' and deploy-style entrypoints). If the exports object has an own property 'fetch', that value must be a function; anything else (a number, string, object, class instance) is rejected before Deno.serve is created.
Source
Thrown at ext/http/00_serve.ts:1640
},
[SymbolAsyncDispose]() {
return this.shutdown();
},
};
}
internals.addTrailers = addTrailers;
internals.serveHttpOnListener = serveHttpOnListener;
internals.serveHttpOnConnection = serveHttpOnConnection;
internals.resetLegacyAbortWarning = () => {
legacyAbortWarned = false;
};
function registerDeclarativeServer(exports) {
if (!ObjectHasOwn(exports, "fetch")) return;
if (typeof exports.fetch !== "function") {
throw new TypeError("Invalid type for fetch: must be a function");
}
if (
exports.onListen !== undefined && typeof exports.onListen !== "function"
) {
throw new TypeError("Invalid type for onListen: must be a function");
}
return ({
servePort,
serveHost,
workerCountWhenMain,
}) => {
const server = Deno.serve({
port: servePort,
hostname: serveHost,
[kLoadBalanced]: workerCountWhenMain == null
? trueView on GitHub (pinned to 89f33cbef2)
Solutions
- Make the exported fetch a function: export const fetch = (req: Request) => new Response('ok'); or export default { fetch(req) { ... } }.
- If 'fetch' is not meant to be a server handler, rename the export (e.g. fetchConfig) so the module does not look like a declarative server.
- Run the file with 'deno run' plus an explicit Deno.serve(handler) call instead of relying on the export-based convention.
Example fix
// before
export const fetch = { handler: (req) => new Response("hi") }; // object, not callable
// after
export default {
fetch(req: Request) {
return new Response("hi");
},
}; Defensive patterns
Strategy: type-guard
Type guard
type ServeExports = { fetch?: (req: Request, info: { remoteAddr; url }) => Response | Promise<Response>; onListen?: (info: { hostname: string; port: number }) => void };
function isValidServeModule(m: ServeExports): boolean { return typeof m.fetch !== "function" ? false : true; } Prevention
- Type entrypoint exports explicitly instead of relying on inference.
- Never name non-handler exports 'fetch' in serve entrypoints.
- Run `deno check` on the entry module in CI to catch non-function exports.
When it happens
Trigger: A main/entry module run via 'deno serve' (or a worker spawned for it) whose default or named export object contains fetch: 42, fetch: 'handler', or fetch bound to a non-callable; accidentally exporting a variable named fetch that shadows the global fetch builtin; export const fetch = someObject.handler missing the .bind.
Common situations: Naming a data field or constant 'fetch' in a module that is also used as a serve entrypoint; refactoring a Request handler into a config object and exporting the object; copy-pasting deploy example code into a module that already exports an unrelated fetch symbol.
Related errors
- Invalid type for onListen: must be a function
- Invalid cron schedule: start=${start}, end=${end}, every=${e
- Both 'cert' and 'key' must be provided to enable HTTPS
- Failed to convert range to tsc text span: {:#}
- Type checking failed when generating declarations:\n{}
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/8e5eaf2c1f664f4e.
Report an issue: GitHub.