{"record":{"id":"02d47bd502890b0d","repo":"denoland/deno","slug":"load-hook-must-return-shortcircuit-true-or-ca","errorCode":null,"errorMessage":"load hook must return { shortCircuit: true } or call nextLoad","messagePattern":"load hook must return (.+?) or call nextLoad","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/01_require.js","lineNumber":857,"sourceCode":"          // transient IO) falls through to Rust default loading via\n          // the load loop, which surfaces a clearer error if the\n          // module truly cannot be loaded.\n          return { source: null, shortCircuit: true };\n        }\n      }\n      // For other schemes (data:, http(s):, etc.) we cannot synchronously\n      // produce source here; fall through to Rust default loading.\n      return { source: null, shortCircuit: true };\n    }\n    const hook = loadHooks[index++];\n    let nextCalled = false;\n    const wrappedNext = (u, c) => {\n      nextCalled = true;\n      return nextLoad(u, c);\n    };\n    const result = hook(loadUrl, currentContext, wrappedNext);\n    if (!nextCalled && !result?.shortCircuit) {\n      throw new TypeError(\n        \"load hook must return { shortCircuit: true } or call nextLoad\",\n      );\n    }\n    return result;\n  }\n\n  const result = nextLoad(fileUrl, context);\n  return { result, effectiveUrl };\n}\n\nfunction _startEsmLoadLoop() {\n  if (esmLoadLoopRunning) return;\n  esmLoadLoopRunning = true;\n  (async () => {\n    while (true) {\n      const pollPromise = op_module_hooks_poll_load();\n      core.unrefOpPromise(pollPromise);\n      const req = await pollPromise;","sourceCodeStart":839,"sourceCodeEnd":875,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/node/polyfills/01_require.js#L839-L875","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nmodule.registerHooks({\n  load(url, context, nextLoad) {\n    if (url.startsWith(\"virtual:\")) {\n      return { source: exportSourceFor(url), format: \"module\", shortCircuit: true };\n    }\n    // missing fallthrough -> TypeError\n  },\n});\n\n// after\nmodule.registerHooks({\n  load(url, context, nextLoad) {\n    if (url.startsWith(\"virtual:\")) {\n      return { source: exportSourceFor(url), format: \"module\", shortCircuit: true };\n    }\n    return nextLoad(url, context);\n  },\n});","handlingStrategy":"validation","validationCode":"// Total load hook: handled URLs short-circuit with source, everything else chains.\nconst loadHook = (url: string, context: object, nextLoad: Function) =>\n  url.startsWith(\"virtual:\")\n    ? { source: virtualSourceFor(url), format: \"module\", shortCircuit: true }\n    : nextLoad(url, context);\n\nmodule.registerHooks({ load: loadHook });","typeGuard":"function isLoadResult(v: unknown): v is { source: string | null; format?: string; shortCircuit?: boolean } {\n  return v != null && typeof v === \"object\" && \"source\" in v;\n}","tryCatchPattern":null,"preventionTips":["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"],"tags":["node-compat","modules","hooks","loaders","validation"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}