denoland/deno · error · TypeError
load hook must return { shortCircuit: true } or call nextLoa
Error message
load hook must return { shortCircuit: true } or call nextLoad What it means
The same contract for the load hook of module.registerHooks(): a load hook must call nextLoad() or return an object with shortCircuit: true carrying the module source (for non-file schemes the runner itself returns { source: null, shortCircuit: true }). Returning undefined without chaining throws this TypeError from the hook runner.
Source
Thrown at ext/node/polyfills/01_require.js:857
// transient IO) falls through to Rust default loading via
// the load loop, which surfaces a clearer error if the
// module truly cannot be loaded.
return { source: null, shortCircuit: true };
}
}
// For other schemes (data:, http(s):, etc.) we cannot synchronously
// produce source here; fall through to Rust default loading.
return { source: null, shortCircuit: true };
}
const hook = loadHooks[index++];
let nextCalled = false;
const wrappedNext = (u, c) => {
nextCalled = true;
return nextLoad(u, c);
};
const result = hook(loadUrl, currentContext, wrappedNext);
if (!nextCalled && !result?.shortCircuit) {
throw new TypeError(
"load hook must return { shortCircuit: true } or call nextLoad",
);
}
return result;
}
const result = nextLoad(fileUrl, context);
return { result, effectiveUrl };
}
function _startEsmLoadLoop() {
if (esmLoadLoopRunning) return;
esmLoadLoopRunning = true;
(async () => {
while (true) {
const pollPromise = op_module_hooks_poll_load();
core.unrefOpPromise(pollPromise);
const req = await pollPromise;View on GitHub (pinned to 89f33cbef2)
Solutions
- For URLs you handle, return { source, format, shortCircuit: true }
- End the hook body with `return nextLoad(url, context);`
- Keep hooks synchronous - registerHooks callbacks must not be async or return promises
Example fix
// before
module.registerHooks({
load(url, context, nextLoad) {
if (url.startsWith("virtual:")) {
return { source: exportSourceFor(url), format: "module", shortCircuit: true };
}
// missing fallthrough -> TypeError
},
});
// after
module.registerHooks({
load(url, context, nextLoad) {
if (url.startsWith("virtual:")) {
return { source: exportSourceFor(url), format: "module", shortCircuit: true };
}
return nextLoad(url, context);
},
}); Defensive patterns
Strategy: validation
Validate before calling
// Total load hook: handled URLs short-circuit with source, everything else chains.
const loadHook = (url: string, context: object, nextLoad: Function) =>
url.startsWith("virtual:")
? { source: virtualSourceFor(url), format: "module", shortCircuit: true }
: nextLoad(url, context);
module.registerHooks({ load: loadHook }); Type guard
function isLoadResult(v: unknown): v is { source: string | null; format?: string; shortCircuit?: boolean } {
return v != null && typeof v === "object" && "source" in v;
} Prevention
- Always include shortCircuit: true alongside source in returned results
- Remember registerHooks callbacks are synchronous - never make them async
- Test each hook with a URL it handles and one it must delegate
When it happens
Trigger: A load hook that handles only some URLs and forgets `return nextLoad(url, context)` on the rest; returning { source } without shortCircuit: true; returning a Promise from the synchronous hook.
Common situations: Custom virtual modules or source transforms (TS/Babel) wired via load hooks; porting async module.register()-style loaders to the synchronous registerHooks API without adjusting control flow.
Related errors
- resolve hook must return { shortCircuit: true } or call next
- Module already loaded
- A file exists at the destination: ${destStr}
- ERR_MISSING_ARGS
- No callback function supplied
AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16).
Data as JSON: /api/errors/02d47bd502890b0d.
Report an issue: GitHub.