← All tasks
javascriptardatan/graphql-tools #8420Not a task: not reproduced

CodeFileLoader's import() rejects absolute Windows paths (ERR_UNSUPPORTED_ESM_URL_SCHEME)

envgap__ardatan__graphql-tools-8420

01 / FAILURE SIGNATURE

As reported upstream

Error: Unable to load from file "C:/.../schema.js": Error [ERR_UNSUPPORTED_ESM_URL_SCHEME]: Only URLs with a scheme in: file, data, and node are supported by the default ESM loader. On Windows, absolute paths must be valid file:// URLs. Received protocol 'c:'
Not a benchmark task.
  • In a clean container the reported failure did not reproduce, or the known fix did not make the project run.

02 / ENVIRONMENT RECIPE

Base commit
89e78a56cab81a92b81cd36d419f0ba28d519bf5
Manifest
packages/loaders/code-file/package.json
Reproduce
Awaiting issue-specific recipe
Run under trace
Awaiting a meaningful runtime command

03 / ORIGINAL ISSUE TEXT

ardatan/graphql-tools #8420 · read the original issue
**Describe the bug**

On native Windows (not WSL), `CodeFileLoader` calls dynamic `import()` directly on a raw absolute filesystem path in two places, and both fail:

1. `esm/load-from-module.js` → `tryToLoadFromExport()`:
   ```js
   const mod = await import(filepath);
   ```
   This runs whenever a schema/document pointer resolves to a `.js`/`.cjs`/`.mjs` file.

2. `esm/index.js` → the loader's own `require` option:
   ```js
   await Promise.all(asArray(options.require).map(m => import(m)));
   ```

Node's `import()` always resolves its specifier through the ESM loader, regardless of whether the calling code is itself CJS or ESM. On Windows, an absolute path like `C:\Users\me\project\schema.js` gets misparsed as a URL with scheme `c:`, so it throws:

```
Error [ERR_UNSUPPORTED_ESM_URL_SCHEME]: Only URLs with a scheme in: file, data, and node are supported by the default ESM loader. Received protocol 'c:'
```

This affects anything that loads a schema or documents from a JS/CJS/MJS module file on Windows via `CodeFileLoader` — e.g. `@graphql-tools/load`, `graphql-config`, or `@graphql-codegen/cli`.

**To Reproduce** Steps to reproduce the behavior:

1. On Windows, create `schema.js`:
   ```js
   const { buildSchema } = require('graphql');

   module.exports = buildSchema(`
     type Query {
       id: ID!
     }
   `);
   ```

2. Load it through `CodeFileLoader` via `@graphql-tools/load`, e.g. `test.mts`:
   ```ts
   import { CodeFileLoader } from '@graphql-tools/code-file-loader';
   import { loadSchema } from '@graphql-tools/load';
   import * as path from 'node:path';

   const filename = path.resolve('schema.js');

   const schema = await loadSchema(filename, {
     loaders: [new CodeFileLoader()],
   });

   console.log(schema);
   ```
   Run with `pnpm tsx test.mts` (or `npx tsx test.mts`).

3. Observe the throw:
   ```
   Error: Unable to load from file "C:/.../schema.js": Error [ERR_UNSUPPORTED_ESM_URL_SCHEME]: Only URLs with a scheme in: file, data, and node are supported by the default ESM loader. On Windows, absolute paths must be valid file:// URLs. Received protocol 'c:'
       at tryToLoadFromExport (.../code-file-loader/esm/load-from-module.js:12:15)
       at async CodeFileLoader.handleSinglePath (.../code-file-loader/esm/index.js:186:32)
   ```

The same failure occurs passing an absolute path via the loader's `require` option.

> **Gotcha while reproducing:** if `schema.js`'s SDL is written as a `/* GraphQL */`-tagged template literal, e.g.:
> ```js
> module.exports = buildSchema(/* GraphQL */ `
>   type Query { id: ID! }
> `);
> ```
> then `graphql-tag-pluck`'s static source-text extraction finds and returns it without ever importing the file, which silently masks this bug (the `!options.noPluck` branch in `handleSinglePath` succeeds first). To reliably hit the `import()` path, either drop that magic comment from the schema source (as shown in step 1 above) or pass `noPluck: true` to `CodeFileLoader`.

**Expected behavior**

The schema/document loads successfully — same behavior as with a relative path, or on POSIX.

**Environment:**

- OS: Windows (Windows 11, native)
- `@graphql-tools/code-file-loader`: 8.1.32
- NodeJS: verified reproducing on Node v24.16.0 (also affects Node 22, per `@graphql-codegen/cli`'s CI)

**Additional context**

Fix is small: convert absolute paths to `file://` URLs via `url.pathToFileURL()` before passing to `import()` (bare specifiers and already-valid URLs pass through unchanged) — `isAbsolute(m) ? pathToFileURL(m).href : m`.

We hit this while fixing Windows CI for `@graphql-codegen/cli`'s test suite and worked around it with a local `pnpm patch` on `code-file-loader@8.1.32` covering both call sites above:
https://github.com/dotansimha/graphql-code-generator/blob/master/patches/%40graphql-tools__code-file-loader%408.1.32.patch

Reference PR (has the patch + full context): https://github.com/dotansimha/graphql-code-generator/pull/10935

Happy to open a PR here with the same fix upstream if that's welcome.

Continue on GitHub ↗

04 / LABELS

Labels from the report text only; not yet run

No supported category has been assigned.

Label rules and the text that matched
[]