Repository navigation
@sentry/nextjs 10.72.0+ throws "The URL must be of scheme file" at module scope under jsdom/happy-dom #23789
Description
Is there an existing issue for this?
- I have checked for existing issues
- I have reviewed the documentation
- I am using the latest SDK release
Searched for fileURLToPath, must be of scheme file, code-transformer-bundler-plugins, and jsdom/happy-dom combinations before filing; nothing matched. #22794 and #23611 look like sibling symptoms of the same structural cause — the server entry eagerly pulling in the orchestrion bundler plugin — but neither covers this crash.
How do you use Sentry?
Sentry SaaS (sentry.io)
Which SDK are you using?
@sentry/nextjs
SDK Version
10.72.0 and 10.73.0 (10.71.0 is fine)
Framework Version
Next.js 16.3.3, Node 24.14.0. Reproduces with no framework at all — see below.
Link to Sentry event
N/A — this is a module-load crash, nothing reaches Sentry.
Reproduction Example/SDK Setup
Importing @sentry/nextjs throws at module scope whenever a document global exists — which is the normal state of affairs under jsdom or happy-dom, i.e. in any Vitest or Jest test using a DOM environment.
No test runner or bundler is needed to see it:
mkdir sentry-repro && cd sentry-repro && npm init -y
npm i @sentry/[email protected]
cat > repro.cjs <<'JS'
globalThis.document = { baseURI: 'http://localhost:3000/' }
require('@sentry/nextjs')
console.log('loaded OK')
JS
node repro.cjs # throws
node -e "require('@sentry/nextjs')" # control: prints loaded OKSteps to Reproduce
- Install
@sentry/[email protected]or@10.73.0. - Define any
documentglobal with abaseURI— or just run a Vitest suite withenvironment: 'happy-dom'(or Jest withtestEnvironment: 'jsdom') that imports the SDK, directly or transitively. - Load the module.
Expected Result
The module loads. 10.71.0 does, under an identical setup.
Actual Result
TypeError [ERR_INVALID_URL_SCHEME]: The URL must be of scheme file
at Object.fileURLToPath (node:internal/url:1608:11)
at Module.<anonymous> (node_modules/@sentry/server-utils/build/cjs/vendored/
@apm-js-collab/code-transformer-bundler-plugins/dist/esm/webpack.js:9:61)
at Module._compile (node:internal/modules/cjs/loader:1812:14)
The node export condition of @sentry/nextjs reaches @sentry/server-utils/build/cjs/orchestrion/bundler/webpack.js, which pulls in the vendored @apm-js-collab/code-transformer-bundler-plugins. That module computes LOADER_PATH at module scope from a dual browser/Node guard:
var LOADER_PATH = path.resolve(path.dirname(url.fileURLToPath(
typeof document === 'undefined'
? pathToFileURL(__filename).href
: (_documentCurrentScript && ... || new URL('vendored/.../webpack.js', document.baseURI).href)
)), "..", "cjs", "webpack-loader.cjs");The guard is typeof document === 'undefined', so it treats "a document exists" as "this is a browser". Under jsdom/happy-dom that is false — the code is running in Node, and __filename is right there and valid. It takes the browser branch, builds http://localhost:3000/vendored/... from document.baseURI, and hands that to fileURLToPath, which only accepts file:.
Two things make this worse than it might look:
- It throws at module scope, so it is not catchable by the importer and takes down the whole module graph. In our suite, 11 test files stopped loading; every assertion inside them still passed, so the failure did not look like a dependency problem at all.
- It arrives through lockfile regeneration, not a reviewed bump. Our
package.jsonsays^10.70.0and never changed; a routine Dependabot PR for unrelated packages re-resolved the tree and moved the SDK from 10.71.0 to 10.72.0 as a side effect.
Suggested fix
Prefer a positive Node check over an absence-of-document check, e.g. gate on typeof __filename !== 'undefined' (or process.versions?.node) first and fall back to the document-based path only when there is genuinely no module path available. The bundler emitting the dual guard is @apm-js-collab/code-transformer-bundler-plugins, so the fix may belong there rather than in this repo — but it ships vendored inside @sentry/server-utils, which is where it reaches users.
Deferring the LOADER_PATH computation until the plugin is actually used would also fix it, and would additionally address the class of problem in #22794 and #23611: a bundler-time plugin should not be doing filesystem path resolution just because someone imported the SDK.
Workaround
Pinning below 10.72.0 ("@sentry/nextjs": "<10.72.0" in pnpm.overrides). Setting resolve.conditions: ['browser'] in Vitest also gets past it, but only by resolving the client build, which then wants next/router — absent in an App Router project — and it changes resolution for every other package too.
Metadata
Metadata
Assignees
Projects
- StatusShow more project fieldsNo status
Activity
linear-code commentedon Aug 31, 2026
JS-3480
chore(deps): update workspace dependencies
terrymun commentedon Sep 1, 2026
I can confirm that upgrading beyond 10.71.0 causes issues with happydom:
chore(deps): update workspace dependencies
cfilipesp1 commentedon Sep 2, 2026
Confirming this independently (Node 22.22.2, Vitest 4.1.11,
jsdomenvironment, Next.js app). Same tree, only the SDK version changing:10.71.0→ 1 passed,10.72.0→ 1 failed,10.73.0→ 1 failed. It took out 4 of our test suites; the remaining ~20,700 tests run under thenodeenvironment and are unaffected.One correction to the root-cause attribution that I think moves the fix squarely into this repo. The issue body suggests the bad guard may belong upstream in
@apm-js-collab/code-transformer-bundler-plugins. Comparing the published tarballs, upstream's CJS build does not have this problem:pathToFileURL(__filename)is always afile:URL, with nodocumentsniffing at all. Up to 10.71.0@sentry/server-utilsdeclared that package independencies, and itsexportsmap routesrequire()to exactly that CJS build — which is why 10.71.0 works under jsdom.In 10.72.0
@sentry/server-utilsdropped the@apm-js-collab/*dependencies and vendored the plugin intobuild/{cjs,esm}/vendored/. The vendoring took the ESM source (dist/esm/webpack.mjs, which usesimport.meta.url) and recompiled it to CJS, and theimport.meta.urlshim emitted by that conversion is the browser-aware one quoted in the issue. So the dual guard is not upstream's code — it is an artifact of vendoring the ESM variant instead of the CJS one that was already being consumed.That makes the minimal fix a build-config change here: vendor
dist/cjs/webpack.cjs, or configure the bundler soimport.meta.urlcompiles to the unconditional Node form (pathToFileURL(__filename)), since this code path only ever runs in Node. DeferringLOADER_PATHuntil the plugin is used is still worth doing for the reasons already given, but it isn't required to unbreak this.Two smaller notes for anyone landing here:
node_modulesand is therefore externalized by Vitest, so Viteresolve.aliasnever intercepts therequire()inside the externalized CJS module. Pinning~10.71.0is the only thing that worked for us.@sentry/nextjsunder a jsdom environment would catch this class of problem — it is invisible to a Node-only test matrix.Generated by Claude Code
s1gr1d commentedon Sep 2, 2026
Thanks for this detailed report! I was looking for the root cause and we're going to fix this.
The regression probably comes from #23667, released in 10.72.0. Before,
@sentry/server-utilspulled in the code-transformer plugin as a regular dependency and loaded that package's own CJS build. Now it bundles a copy of the plugin inside itself, and that copy is compiled from ESM to CJS.The tool that does that compilation (rollup) replaces
import.meta.urlwith a stock snippet that assumes "if adocumentglobal exists, we must be in a browser." That's normally a safe guess. But jsdom and happy-dom define a fakedocumentwhile your tests run in Node. So the SDK seesdocument, concludes it's in a browser, and tries to converthttp://localhost:3000/...into a file path. That fails, and the import crashes.On 10.71.0 and earlier,
require()loaded the plugin's own CJS build, which uses__filenameto figure out where it is and never checksdocumentat all.So the bundling change made Sentry mistake your test environment for a browser, because jsdom fakes the
documentglobal.3 remaining items
cfilipesp1 commentedon Sep 3, 2026
fix(nextjs): Add orchestrion bundling regression tests and import.met…
chore: update all deps, resume page, strict lint, mobile type fixes
chore: update all deps, resume page, strict lint, mobile type fixes (#…
chore: update all deps, resume page, strict lint, mobile type fixes (…
chore(deps): update patch/minor dependencies (pubky#2461)
fix(test): guard Sentry orchestrion plugin import under happy-dom
document) #24217 as a duplicate of this issuefix: credential rotation, model provenance and validation gates (#2604)
docs: align Keeper guarantees and Sentry test guidance with implement…
github-actions commentedon Sep 23, 2026
A PR closing this issue has just been released 🚀
This issue was referenced by PR #23935, which was included in the 11.0.0 release.