{"record":{"id":"b273bc59aa4e729e","repo":"facebook/react","slug":"expected-getsource-to-have-been-called-before-tran","errorCode":null,"errorMessage":"Expected getSource to have been called before transformSource","messagePattern":"Expected getSource to have been called before transformSource","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/react-server-dom-esm/src/ReactFlightESMNodeLoader.js","lineNumber":630,"sourceCode":"        ) +\n        ');';\n    }\n    newSrc += '},';\n    newSrc += JSON.stringify(url) + ',';\n    newSrc += JSON.stringify(name) + ');\\n';\n  }\n\n  // TODO: Generate source maps for Client Reference functions so they can point to their\n  // original locations.\n  return newSrc;\n}\n\nasync function loadClientImport(\n  url: string,\n  defaultTransformSource: TransformSourceFunction,\n): Promise<{format: string, shortCircuit?: boolean, source: Source}> {\n  if (stashedGetSource === null) {\n    throw new Error(\n      'Expected getSource to have been called before transformSource',\n    );\n  }\n  // TODO: Validate that this is another module by calling getFormat.\n  const {source} = await stashedGetSource(\n    url,\n    {format: 'module'},\n    stashedGetSource,\n  );\n  const result = await defaultTransformSource(\n    source,\n    {format: 'module', url},\n    defaultTransformSource,\n  );\n  return {format: 'module', source: result.source};\n}\n\nasync function transformModuleIfNeeded(","sourceCodeStart":612,"sourceCodeEnd":648,"githubUrl":"https://github.com/facebook/react/blob/eafeac097ba51e1eab809c07102126bd5f8e5425/packages/react-server-dom-esm/src/ReactFlightESMNodeLoader.js#L612-L648","documentation":"react-server-dom-esm ships a Node module-customization loader (package export 'react-server-dom-esm/node-loader'). Its getSource hook stashes the chain's default getSource so that later, when transformSource rewrites a module that imports a 'use client' reference, loadClientImport can re-fetch that imported module's raw source. This error means transformSource ran while stashedGetSource was still null: the loader's transformSource hook executed in a hook chain where its own getSource hook was never called.","triggerScenarios":"Importing a module that contains a client-reference import through the ESM loader when the loader's getSource hook never ran: another loader in the chain short-circuits getSource without delegating to its default, hooks are registered in an order that skips React's getSource, the hooks are invoked manually instead of via --experimental-loader/register, or a Node version with a different hook protocol calls only part of the chain.","commonSituations":"Chaining --experimental-loader flags (tsx/esbuild plus React's loader) in the wrong order; registering the loader after app modules were already resolved; upgrading Node majors where getSource/transformSource semantics changed; test runners that bypass or partially apply the loader chain.","solutions":["Register the loader exactly once via its documented entrypoint before any app import: node --conditions react-server --experimental-loader ./node_modules/react-server-dom-esm/esm/react-server-dom-esm-node-loader.production.js server.js, or register('react-server-dom-esm/node-loader', import.meta.url) from node:module","Remove or reorder other loaders so React's getSource actually runs; every other loader in the chain must delegate to its default* function instead of short-circuiting","Run a Node version supported by the loader; if your Node no longer calls getSource, make sure the loader's load hook path is used instead of transformSource","Never invoke getSource/transformSource by hand; let Node drive the hook order for every module"],"exampleFix":"// before — a second loader swallows getSource, React's transformSource runs un-stashed\nimport {register} from 'node:module';\nregister('react-server-dom-esm/node-loader', import.meta.url);\nregister('tsx/esm', import.meta.url); // runs first and never delegates getSource\nawait import('./app.js');\n\n// after — register React's loader so its hooks own the chain, app loads after\nimport {register} from 'node:module';\nregister('react-server-dom-esm/node-loader', import.meta.url);\nawait import('./app.js');","handlingStrategy":"validation","validationCode":"// entry.mjs — the ONLY place the loader is registered; runs before any app import\nimport {register} from 'node:module';\nregister('react-server-dom-esm/node-loader', import.meta.url);\n\n// Smoke-check the wiring: importing a module with a 'use client' import\n// through the hooks throws here (in CI) instead of in production paths.\nawait import('./smoke-client-import.js');\nawait import('./app.js');","typeGuard":null,"tryCatchPattern":"try {\n  await import('./app.js');\n} catch (e) {\n  if (e instanceof Error && /Expected getSource/.test(e.message)) {\n    console.error('React ESM loader not registered before app import');\n  }\n  throw e;\n}","preventionTips":["Register the loader in a single bootstrap file as the first statement, before any static app import","Pin the same Node major in dev, CI, and production","Never chain loaders that do not delegate to their default* hooks","Add a CI smoke test that imports a module containing a client reference through the loader"],"tags":["react-server-components","node-esm","module-loader","node","rsc"],"backgroundTag":"esm-loader-hook-misconfiguration","analyzedSha":"eafeac097ba51e1eab809c07102126bd5f8e5425","analyzedAt":"2026-08-21T22:01:08.818Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}