openclaw - ✅(Solved) Fix [Bug]: ERR_UNSUPPORTED_ESM_URL_SCHEME on Windows — plugin loader passes raw drive-letter paths to import() [2 pull requests, 4 comments, 5 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#61795Fetched 2026-04-08 02:54:23
View on GitHub
Comments
4
Participants
5
Timeline
16
Reactions
0
Timeline (top)
referenced ×7commented ×4cross-referenced ×2labeled ×2

OpenClaw version: 2026.4.5 (3e72c03) Node.js version: v22.22.2 (also reproduced on v24.14.0) OS: Windows 11 (10.0.26200)

Running openclaw onboard on native Windows crashes immediately after selecting a model. The ESM loader rejects the module specifier because it receives a raw Windows path (C:...) instead of a file:/// URL.

Steps to reproduce

  1. Install openclaw globally on Windows: npm install -g openclaw
  2. Run openclaw onboard
  3. Select Ollama as the provider
  4. Select any model (e.g. ollama/gemma4:e4b)

Error

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:'

Root cause

The plugin/extension loader in dist/loader-BkajlJCF.js resolves plugin paths to absolute Windows paths (e.g. C:\Users...\extensions\openclaw-web-search) and passes them directly to jiti / dynamic import(). On Windows, ESM requires these to be file:///C:/... URLs.

Relevant lines in dist/loader-BkajlJCF.js:

  • Line ~2526: mod = getJiti(safeSource)(safeSource);
  • Line ~2213: const runtimeModule = getJiti(runtimeModulePath)(runtimeModulePath);

Also in dist/io-CS2J_l4V.js:

  • Line ~161: mod = getJiti(contractSource)(contractSource);

Suggested fix

Wrap paths with pathToFileURL() before passing to dynamic import:

import { pathToFileURL } from 'node:url';

// Before mod = getJiti(safeSource)(safeSource);

// After mod = getJiti(safeSource)(pathToFileURL(safeSource).href);

Workaround

Running openclaw under WSL2 avoids the issue since Linux paths don't have drive letters.

Error Message

Error 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:' Error [ERR_UNSUPPORTED_ESM_URL_SCHEME]: Only URLs with a scheme in: file, data, and node are supported by the default ESM

  • Node.js: v22.22.2 (also tested on v24.14.0 — same error)

Root Cause

Running openclaw onboard on native Windows crashes immediately after selecting a model. The ESM loader rejects the module specifier because it receives a raw Windows path (C:...) instead of a file:/// URL.

Fix Action

Fix / Workaround

Workaround

PR fix notes

PR #61832: fix(windows): wrap plugin loader paths with pathToFileURL to fix ERR_UNSUPPORTED_ESM_URL_SCHEME

Description (problem / solution / changelog)

Problem

On Windows, the Node.js ESM loader rejects raw absolute paths like C:\Users\... with:

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:'

The plugin loader in src/plugins/loader.ts passed raw Windows paths directly to getJiti()(path) in three call sites, causing openclaw onboard, interactive setup, and the Control UI to crash on Windows immediately after a provider/model is selected.

This was independently reported in at least three separate issues today alone:

  • Closes #61795
  • Closes #61810
  • Closes #61792

Root Cause

The getJiti(safeSource)(safeSource) and getJiti(runtimeModulePath)(runtimeModulePath) calls receive a bare Windows absolute path (e.g. C:\Users\...\extensions\openclaw-web-search) which the ESM loader misinterprets as an unknown URL scheme c:.

Fix

Adds a toSafeImportPath() helper that converts Windows absolute paths to file:// URLs using Node's built-in pathToFileURL() from node:url. The conversion is only applied on win32 — POSIX paths are returned unchanged, so Linux/macOS behaviour is completely unaffected.

// Before
mod = getJiti(safeSource)(safeSource);

// After  
const safeImportSource = toSafeImportPath(safeSource); // file:///C:/... on Windows
mod = getJiti(safeImportSource)(safeImportSource);

All three affected call sites in loadOpenClawPlugins (plugin module load), loadOpenClawPluginCliRegistry (CLI metadata load), and resolveCreatePluginRuntime (runtime module load) are patched.

Testing

  • POSIX: no change in path handling (process.platform !== 'win32' branch is a no-op)
  • Windows: C:\... paths are converted to file:///C:/... before being passed to jiti/ESM loader

Workaround until merged: use WSL2 on Windows (raw Linux paths avoid the issue).

Changed files

  • src/plugins/loader.ts (modified, +28/-3)

PR #62444: fix(windows): resolve ERR_UNSUPPORTED_ESM_URL_SCHEME on native Windows

Description (problem / solution / changelog)

Summary

  • Problem: openclaw onboard and other plugin-loading code paths crash on native Windows with ERR_UNSUPPORTED_ESM_URL_SCHEME: Received protocol 'c:'. Jiti's native import path passes resolved absolute Windows paths (C:\Users\...) directly to import(), which Node's ESM loader rejects because it interprets the drive letter as an unknown URL scheme.
  • Why it matters: OpenClaw is completely unusable on native Windows — onboarding, model selection, and gateway startup all fail.
  • What changed: Three-layer fix: (1) ESM loader hook (windows-esm-fix.mjs) registered at startup converts drive-letter specifiers to file:// URLs; (2) toSafeImportPath() shared helper wraps all getJiti() call sites; (3) build-time symlink fallback copies files on Windows when EPERM. Also fixes pre-existing TypeScript strict-mode errors in memory-core that blocked pnpm build on a clean checkout.
  • What did NOT change: No changes to non-Windows behavior. toSafeImportPath() is a no-op when process.platform !== "win32". The ESM loader hook only registers on Windows.

Change Type (select all)

  • Bug fix
  • Refactor required for the fix

Scope (select all touched areas)

  • Gateway / orchestration
  • Integrations
  • CI/CD / infra

Linked Issue/PR

  • Closes #61795
  • Related #61832
  • This PR fixes a bug or regression

Root Cause (if applicable)

  • Root cause: Jiti's nativeImportOrRequire in jiti.mjs calls import() with resolved absolute paths. On non-Windows this works because /absolute/path is a valid URL path. On Windows, C:/Users/... is parsed as scheme c:, which Node rejects. The existing fix in #61832 only patched the getJiti()(path) call sites in loader.ts, missing: (a) 9 other files with identical getJiti() patterns, and (b) jiti's own internal import() which is unreachable from application code.
  • Missing detection / guardrail: No Windows CI lane for openclaw onboard integration smoke test.
  • Contributing context: The tryNative: true jiti option triggers a code path in jiti.mjs that lacks the pathToFileURL() guard present in jiti.cjs. Additionally, the build itself fails on Windows without Developer Mode due to file symlink EPERM errors.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/plugins/loader.test.ts"converts Windows absolute import specifiers"
  • Scenario the test should lock in: toSafeImportPath("C:\\Users\\alice\\plugin\\index.mjs") returns "file:///C:/Users/alice/plugin/index.mjs"
  • Why this is the smallest reliable guardrail: The unit test validates the helper that all call sites depend on. The remaining gap is an e2e Windows onboard smoke test.
  • Existing test that already covers this: Yes — the test from #61832 validates toSafeImportPath. This PR moves the function to sdk-alias.ts (shared) and reuses it across all loader files.
  • If no new test is added, why not: The existing unit test covers the helper. A full Windows e2e onboard smoke test is out of scope for this fix.

User-visible / Behavior Changes

openclaw onboard, openclaw doctor --fix, and gateway startup now complete successfully on native Windows 11. No changes to behavior on Linux/macOS.

Diagram (if applicable)

Before:
[jiti resolves path] -> C:\Users\...\plugin.js -> import("C:/Users/.../plugin.js") -> ERR_UNSUPPORTED_ESM_URL_SCHEME

After (layer 1 - ESM hook in openclaw.mjs):
[jiti resolves path] -> C:\Users\...\plugin.js -> import("C:/Users/.../plugin.js")
  -> windows-esm-fix.mjs resolve() -> import("file:///C:/Users/.../plugin.js") -> OK

After (layer 2 - toSafeImportPath at call sites):
[getJiti(path)] -> getJiti(path)(toSafeImportPath(path)) -> jiti receives file:///... -> OK

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No

Repro + Verification

Environment

  • OS: Windows 11 Pro 10.0.26200
  • Runtime: Node.js v22.20.0, pnpm 10.32.1
  • Model/provider: anthropic/claude-opus-4-6 via setup-token
  • Integration/channel: Telegram
  • Relevant config: Default QuickStart config, loopback gateway on port 18789

Steps

  1. Build from source on Windows: pnpm install && pnpm build
  2. Run openclaw onboard
  3. Select Anthropic provider, choose auth method, select a model

Expected

Onboarding completes through channel selection, skills, hooks, and gateway setup.

Actual (before fix)

Crashes with 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:' immediately after model selection.

Evidence

  • Trace/log snippets — Custom ESM loader hook captured the exact bad specifiers from jiti:
    === BAD SPECIFIER FOUND ===
    Specifier: C:/Users/ozgur/Documents/openclaw/dist/agents/models-config.runtime.js
    Parent URL: file:///C:/Users/ozgur/Documents/openclaw/node_modules/jiti/lib/jiti.mjs
    Conditions: ["node","import","module-sync","node-addons"]
    === END ===
  • Failing before + passing after — openclaw onboard completes fully after the fix, including model selection, channel config, skills install, hooks setup, and gateway service install.

Human Verification (required)

  • Openclaw onboard tested on windows 11.
  • Verified scenarios: Full openclaw onboard on Windows 11 with Anthropic setup-token auth, Telegram channel, Brave Search, skills install (4 skills), hooks enable (4 hooks), gateway service reinstall. Also verified openclaw doctor --fix loads all 54 plugins with 0 errors.
  • Edge cases checked: openclaw --version, openclaw --help, openclaw doctor --fix (plugin loading), multiple onboard runs with different auth methods (CLI and setup-token).
  • What you did not verify: Linux/macOS regression (no-op on non-Windows by design), WSL2, Docker/Podman builds.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No

Risks and Mitigations

  • Risk: windows-esm-fix.mjs ESM loader hook intercepts all resolve() calls on Windows, adding a regex test per specifier.
    • Mitigation: The regex /^[a-zA-Z]:/ is trivially fast and only fires on Windows. Non-matching specifiers pass through with zero transformation. The hook is not registered on non-Windows platforms.
  • Risk: tryNative: false on Windows means jiti uses its transform path instead of Node's native ESM loader for plugin loading.
    • Mitigation: This matches the existing shouldPreferNativeJiti() which already returns false on Windows. The ESM loader hook is the primary fix; tryNative: false is defense-in-depth.

Changed files

  • openclaw.mjs (modified, +11/-0)
  • package.json (modified, +1/-0)
  • scripts/stage-bundled-plugin-runtime.mjs (modified, +25/-3)
  • src/channels/plugins/module-loader.ts (modified, +2/-1)
  • src/plugin-sdk/channel-entry-contract.ts (modified, +3/-2)
  • src/plugin-sdk/facade-loader.ts (modified, +2/-1)
  • src/plugins/bundled-capability-runtime.ts (modified, +2/-1)
  • src/plugins/bundled-channel-config-metadata.ts (modified, +2/-2)
  • src/plugins/doctor-contract-registry.ts (modified, +2/-1)
  • src/plugins/loader.test.ts (modified, +23/-0)
  • src/plugins/loader.ts (modified, +1/-26)
  • src/plugins/public-surface-loader.ts (modified, +2/-1)
  • src/plugins/runtime/runtime-plugin-boundary.ts (modified, +2/-1)
  • src/plugins/sdk-alias.ts (modified, +35/-1)
  • src/plugins/setup-registry.ts (modified, +4/-3)
  • windows-esm-fix.mjs (added, +11/-0)

Code Example

$ openclaw onboard
  # ... wizard steps complete normally until model selection ...

Default model
  │  ollama/gemma4:e4b
  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:'
  

  - OpenClaw version: 2026.4.5 (3e72c03)
  - Node.js: v22.22.2 (also tested on v24.14.0 — same error)
  - OS: Windows 11 (10.0.26200)
  - Install: `npm install -g openclaw`

  **Root cause:** The plugin loader in `dist/loader-BkajlJCF.js` passes raw Windows absolute paths (e.g. `C:\Users\...`) to
  dynamic `import()`. ESM on Windows requires `file:///C:/...` URLs. The fix is to wrap paths with `pathToFileURL()` from
  `node:url` before importing.

  Affected lines:
  - `dist/loader-BkajlJCF.js` ~L2526, ~L2213
  - `dist/io-CS2J_l4V.js` ~L161
RAW_BUFFERClick to expand / collapse

Bug type

Crash (process/app exits or hangs)

Beta release blocker

No

Summary

OpenClaw version: 2026.4.5 (3e72c03) Node.js version: v22.22.2 (also reproduced on v24.14.0) OS: Windows 11 (10.0.26200)

Running openclaw onboard on native Windows crashes immediately after selecting a model. The ESM loader rejects the module specifier because it receives a raw Windows path (C:...) instead of a file:/// URL.

Steps to reproduce

  1. Install openclaw globally on Windows: npm install -g openclaw
  2. Run openclaw onboard
  3. Select Ollama as the provider
  4. Select any model (e.g. ollama/gemma4:e4b)

Error

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:'

Root cause

The plugin/extension loader in dist/loader-BkajlJCF.js resolves plugin paths to absolute Windows paths (e.g. C:\Users...\extensions\openclaw-web-search) and passes them directly to jiti / dynamic import(). On Windows, ESM requires these to be file:///C:/... URLs.

Relevant lines in dist/loader-BkajlJCF.js:

  • Line ~2526: mod = getJiti(safeSource)(safeSource);
  • Line ~2213: const runtimeModule = getJiti(runtimeModulePath)(runtimeModulePath);

Also in dist/io-CS2J_l4V.js:

  • Line ~161: mod = getJiti(contractSource)(contractSource);

Suggested fix

Wrap paths with pathToFileURL() before passing to dynamic import:

import { pathToFileURL } from 'node:url';

// Before mod = getJiti(safeSource)(safeSource);

// After mod = getJiti(safeSource)(pathToFileURL(safeSource).href);

Workaround

Running openclaw under WSL2 avoids the issue since Linux paths don't have drive letters.

Steps to reproduce

  1. Install openclaw globally on Windows: npm install -g openclaw
  2. Run openclaw onboard
  3. Select Ollama as the provider
  4. Select any model (e.g. ollama/gemma4:e4b)

Expected behavior

openclaw onboard should complete the setup wizard successfully on native Windows without errors, and the selected model should be saved to the config.

Actual behavior

The setup wizard crashes with ERR_UNSUPPORTED_ESM_URL_SCHEME immediately after selecting the default model. The config is not updated and onboarding fails to complete.

OpenClaw version

2026.4.5 (3e72c03)

Operating system

Windows 11 (10.0.26200)

Install method

npm install -g openclaw

Model

ollama/gemma4:e4b

Provider / routing chain

openclaw -> ollama (local)

Additional provider/model setup details

No response

Logs, screenshots, and evidence

$ openclaw onboard
  # ... wizard steps complete normally until model selection ...

  ◇  Default model
  │  ollama/gemma4:e4b
  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:'
  

  - OpenClaw version: 2026.4.5 (3e72c03)
  - Node.js: v22.22.2 (also tested on v24.14.0 — same error)
  - OS: Windows 11 (10.0.26200)
  - Install: `npm install -g openclaw`

  **Root cause:** The plugin loader in `dist/loader-BkajlJCF.js` passes raw Windows absolute paths (e.g. `C:\Users\...`) to
  dynamic `import()`. ESM on Windows requires `file:///C:/...` URLs. The fix is to wrap paths with `pathToFileURL()` from
  `node:url` before importing.

  Affected lines:
  - `dist/loader-BkajlJCF.js` ~L2526, ~L2213
  - `dist/io-CS2J_l4V.js` ~L161

Impact and severity

No response

Additional information

No response

extent analysis

TL;DR

Wrap Windows paths with pathToFileURL() before passing to dynamic import to fix the ERR_UNSUPPORTED_ESM_URL_SCHEME error.

Guidance

  • Identify the lines of code that pass absolute Windows paths to dynamic import(), specifically in dist/loader-BkajlJCF.js and dist/io-CS2J_l4V.js.
  • Import pathToFileURL from node:url to convert Windows paths to valid file:/// URLs.
  • Update the affected lines to use pathToFileURL() before importing, for example: mod = getJiti(pathToFileURL(safeSource).href).
  • Verify the fix by running openclaw onboard and completing the setup wizard without errors.

Example

import { pathToFileURL } from 'node:url';

// Before
mod = getJiti(safeSource)(safeSource);

// After
mod = getJiti(safeSource)(pathToFileURL(safeSource).href);

Notes

This fix assumes that the issue is solely due to the ERR_UNSUPPORTED_ESM_URL_SCHEME error caused by passing raw Windows paths to dynamic import(). If other errors occur, additional debugging may be necessary.

Recommendation

Apply the workaround by wrapping Windows paths with pathToFileURL() to fix the immediate issue, as this is a straightforward and targeted solution.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

openclaw onboard should complete the setup wizard successfully on native Windows without errors, and the selected model should be saved to the config.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING