openclaw - 💡(How to fix) Fix [Bug]: CLI plugin loader dual-evaluates own `dist/*.js` chunks via jiti (~3s wasted per invocation, duplicate class identities) [1 pull requests]

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…

buildPluginLoaderJitiOptions (src/plugins/sdk-alias.ts) sets tryNative: true so jiti prefers Node's native loader for .js artifacts, and this works for the top-level target. However, when jiti is invoked for any other reason (a TS plugin entry, an alias-rewrite request, or tryNative: false from bundled-capability-runtime), its module evaluation processes the transitive static-import graph through jiti's own module cache. The graph reaches sibling dist chunks (e.g. dist/gateway/call.jsdist/gateway/client.js), which Node has already loaded into its native ESM cache. Result: each openclaw dist module gets two separate import.meta records — one native, one in jiti's cache — with different class identities.

This shows up plainly when probing a single CLI invocation: a top-of-file marker in dist/client-CBm35uE_.js fires twice in the same process, on every openclaw devices list / openclaw agents list / etc.

Error Message

sed -i '1i console.error("MARKER-IMPORT t=" + Date.now() + " url=" + import.meta.url);' \

Root Cause

This is purely a duplicate-evaluation / wasted-work fix. It does not affect the unrelated gateway timeout after 10000ms hang on openclaw devices list / openclaw agents list that we are seeing on the same host — that has a separate root cause (the connect response is not delivered back through the loopback WS in our current envy state) and will get its own issue once isolated.

Fix Action

Fixed

Code Example

# Instrument the bundled gateway client with a side-effect marker
sed -i '1i console.error("MARKER-IMPORT t=" + Date.now() + " url=" + import.meta.url);' \
  "$(npm root -g)/openclaw/dist/client-CBm35uE_.js"

# Run any WS-protocol CLI subcommand
openclaw devices list 2> /tmp/cli.err

# Observe two MARKER-IMPORT lines:
grep -c MARKER-IMPORT /tmp/cli.err   # → 2

# The second has a jiti stack:
grep MARKER-IMPORT /tmp/cli.err | tail -1
# ... at jitiRequire (.../jiti.cjs:1:...) ... ./call-DC9-4tbc.js ... openclaw-tools-DJxO67FO.js ...
RAW_BUFFERClick to expand / collapse

Summary

buildPluginLoaderJitiOptions (src/plugins/sdk-alias.ts) sets tryNative: true so jiti prefers Node's native loader for .js artifacts, and this works for the top-level target. However, when jiti is invoked for any other reason (a TS plugin entry, an alias-rewrite request, or tryNative: false from bundled-capability-runtime), its module evaluation processes the transitive static-import graph through jiti's own module cache. The graph reaches sibling dist chunks (e.g. dist/gateway/call.jsdist/gateway/client.js), which Node has already loaded into its native ESM cache. Result: each openclaw dist module gets two separate import.meta records — one native, one in jiti's cache — with different class identities.

This shows up plainly when probing a single CLI invocation: a top-of-file marker in dist/client-CBm35uE_.js fires twice in the same process, on every openclaw devices list / openclaw agents list / etc.

Impact

  • ~3 s per CLI invocation of redundant module evaluation on a 45-plugin install (each plugin and every transitively-imported dist chunk is loaded twice). Larger plugin installs make it worse.
  • Duplicate class identities for any class defined in a re-evaluated chunk. In our trace this includes GatewayClient, GatewayTransportError, and every type-class in the gateway-call subtree. Today only one instance is constructed, but any future code that relies on instanceof against a re-imported class — or on a module-level singleton — will silently misbehave.
  • Stale-state risk: a module that holds module-level mutable state (e.g. a registry Map) silently splits if any path reaches the second copy.

Reproduction

# Instrument the bundled gateway client with a side-effect marker
sed -i '1i console.error("MARKER-IMPORT t=" + Date.now() + " url=" + import.meta.url);' \
  "$(npm root -g)/openclaw/dist/client-CBm35uE_.js"

# Run any WS-protocol CLI subcommand
openclaw devices list 2> /tmp/cli.err

# Observe two MARKER-IMPORT lines:
grep -c MARKER-IMPORT /tmp/cli.err   # → 2

# The second has a jiti stack:
grep MARKER-IMPORT /tmp/cli.err | tail -1
# ... at jitiRequire (.../jiti.cjs:1:...) ... ./call-DC9-4tbc.js ... openclaw-tools-DJxO67FO.js ...

Environment

  • OpenClaw 2026.5.19 (a185ca2), Node v24.15.0, x86_64 Linux Mint, jiti 2.7.0.
  • Also confirmed identical dist artifacts on OpenClaw 2026.5.19-beta.2 / Node v22.22.2 / Pi 5 aarch64 — the configuration is the same; whether the second load triggers depends on the workload reaching a jiti-loaded chunk, but the latent split exists everywhere.

Proposed fix

Add nativeModules: ["openclaw"] to the jiti options returned by buildPluginLoaderJitiOptions. jiti's nativeModules is an "always use native require()" list for paths inside the named packages — exactly what we want for our own dist artifacts. After the change the marker fires once per CLI invocation. PR follows.

Not addressed

This is purely a duplicate-evaluation / wasted-work fix. It does not affect the unrelated gateway timeout after 10000ms hang on openclaw devices list / openclaw agents list that we are seeing on the same host — that has a separate root cause (the connect response is not delivered back through the loopback WS in our current envy state) and will get its own issue once isolated.

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