openclaw - 💡(How to fix) Fix ERR_UNSUPPORTED_ESM_URL_SCHEME on Windows: jiti tryNative passes bare paths to import() [4 comments, 4 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#61853Fetched 2026-04-08 02:53:30
View on GitHub
Comments
4
Participants
4
Timeline
6
Reactions
0
Timeline (top)
commented ×4closed ×1cross-referenced ×1

Error Message

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

buildPluginLoaderJitiOptions() in sdk-alias hardcodes tryNative: true. On Windows, when jiti uses its native import path, it passes raw filesystem paths (e.g., C:\Users\...\setup-api.js) directly to import(), which requires file:/// URLs on Windows.

Most jiti loader sites in the codebase correctly override this with shouldPreferNativeJiti(modulePath) (which returns false on Windows), but at least two do not:

  1. setup-registry getJiti() — uses buildPluginLoaderJitiOptions(aliasMap) without tryNative override (the cache key on the line above does use shouldPreferNativeJiti, but the actual jiti options do not)
  2. io getJiti() (doctor contracts) — same pattern

The crash happens during onboarding because resolvePluginSetupProviderresolvePluginSetupRegistry iterates discovered plugins and loads their setup-api modules through this code path.

Fix Action

Workaround

Register a custom ESM loader hook via --import that converts bare Windows paths to file:// URLs before they reach Node's ESM loader:

// win-esm-fix.mjs
import { pathToFileURL } from "node:url";
const WINDOWS_ABS_PATH = /^[a-zA-Z]:[/\]/;

export function resolve(specifier, context, nextResolve) {
  if (WINDOWS_ABS_PATH.test(specifier)) {
    return nextResolve(pathToFileURL(specifier).href, context);
  }
  return nextResolve(specifier, context);
}

Then launch with: node --import ./win-esm-register.mjs openclaw.mjs

Code Example

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

---

const loader = createJiti(modulePath, buildPluginLoaderJitiOptions(aliasMap));

---

const loader = createJiti(modulePath, {
    ...buildPluginLoaderJitiOptions(aliasMap),
    tryNative: shouldPreferNativeJiti(modulePath)
});

---

// win-esm-fix.mjs
import { pathToFileURL } from "node:url";
const WINDOWS_ABS_PATH = /^[a-zA-Z]:[/\]/;

export function resolve(specifier, context, nextResolve) {
  if (WINDOWS_ABS_PATH.test(specifier)) {
    return nextResolve(pathToFileURL(specifier).href, context);
  }
  return nextResolve(specifier, context);
}
RAW_BUFFERClick to expand / collapse

Bug

openclaw onboard --install-daemon crashes on Windows after the "Default model" configuration step 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:'

Environment

  • Windows 11 Pro
  • Node.js v22.22.2 (also reproduces on v24.14.1)
  • OpenClaw 2026.4.5

Root cause

buildPluginLoaderJitiOptions() in sdk-alias hardcodes tryNative: true. On Windows, when jiti uses its native import path, it passes raw filesystem paths (e.g., C:\Users\...\setup-api.js) directly to import(), which requires file:/// URLs on Windows.

Most jiti loader sites in the codebase correctly override this with shouldPreferNativeJiti(modulePath) (which returns false on Windows), but at least two do not:

  1. setup-registry getJiti() — uses buildPluginLoaderJitiOptions(aliasMap) without tryNative override (the cache key on the line above does use shouldPreferNativeJiti, but the actual jiti options do not)
  2. io getJiti() (doctor contracts) — same pattern

The crash happens during onboarding because resolvePluginSetupProviderresolvePluginSetupRegistry iterates discovered plugins and loads their setup-api modules through this code path.

Suggested fix

In setup-registry and io, change:

const loader = createJiti(modulePath, buildPluginLoaderJitiOptions(aliasMap));

to:

const loader = createJiti(modulePath, {
    ...buildPluginLoaderJitiOptions(aliasMap),
    tryNative: shouldPreferNativeJiti(modulePath)
});

This matches the pattern already used correctly in loader, bootstrap-registry, channel-entry-contract, config-presence, facade-runtime, and other jiti instantiation sites.

Alternatively, buildPluginLoaderJitiOptions() itself could default tryNative based on platform instead of hardcoding true.

Workaround

Register a custom ESM loader hook via --import that converts bare Windows paths to file:// URLs before they reach Node's ESM loader:

// win-esm-fix.mjs
import { pathToFileURL } from "node:url";
const WINDOWS_ABS_PATH = /^[a-zA-Z]:[/\]/;

export function resolve(specifier, context, nextResolve) {
  if (WINDOWS_ABS_PATH.test(specifier)) {
    return nextResolve(pathToFileURL(specifier).href, context);
  }
  return nextResolve(specifier, context);
}

Then launch with: node --import ./win-esm-register.mjs openclaw.mjs

extent analysis

TL;DR

Update the buildPluginLoaderJitiOptions function to conditionally set tryNative based on the platform or override it in the setup-registry and io modules to fix the ESM loader issue on Windows.

Guidance

  • Identify and update the setup-registry and io modules to override the tryNative option in the buildPluginLoaderJitiOptions function, as shown in the suggested fix.
  • Alternatively, consider modifying the buildPluginLoaderJitiOptions function itself to default tryNative based on the platform.
  • To verify the fix, run the openclaw onboard --install-daemon command again and check for the absence of the ERR_UNSUPPORTED_ESM_URL_SCHEME error.
  • If the issue persists, consider implementing the custom ESM loader hook workaround as a temporary solution.

Example

const loader = createJiti(modulePath, {
  ...buildPluginLoaderJitiOptions(aliasMap),
  tryNative: shouldPreferNativeJiti(modulePath)
});

Notes

The suggested fix assumes that the shouldPreferNativeJiti function correctly determines whether to use native imports based on the platform. If this function is not implemented or incorrect, additional modifications may be necessary.

Recommendation

Apply the suggested fix by updating the setup-registry and io modules to override the tryNative option, as this approach is more targeted and less likely to introduce unintended side effects.

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…

Still need to ship something?

×6

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

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix ERR_UNSUPPORTED_ESM_URL_SCHEME on Windows: jiti tryNative passes bare paths to import() [4 comments, 4 participants]