openclaw - ✅(Solved) Fix [Bug]: CLI startup extremely slow (~42s) due to jiti scanning workspace repos [1 pull requests, 1 comments, 2 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#69758Fetched 2026-04-22 07:48:36
View on GitHub
Comments
1
Participants
2
Timeline
3
Reactions
0
Author
Participants
Timeline (top)
commented ×1cross-referenced ×1subscribed ×1

openclaw status takes ~42 seconds to complete, with 55% of JS execution time spent in jiti.normalizeAliases scanning and transpiling TypeScript files from workspace directories that are irrelevant to the CLI command.

Root Cause

openclaw status takes ~42 seconds to complete, with 55% of JS execution time spent in jiti.normalizeAliases scanning and transpiling TypeScript files from workspace directories that are irrelevant to the CLI command.

Fix Action

Fix / Workaround

Additional information

  • NODE_COMPILE_CACHE provides only marginal improvement (42s → 40s)
  • The core issue is jiti doing runtime TS→JS transpilation of files in workspace subdirectories that have nothing to do with OpenClaw configuration
  • Suggested fixes:
    1. Exclude repos/, node_modules/, and mission-control/ from jiti scan paths
    2. Support a .openclawignore or similar mechanism to exclude directories
    3. Consider lazy-loading jiti only when actually needed (e.g. config file parsing)
    4. Pre-compile config TS files at install time instead of runtime transpilation
  • Workaround: None practical without moving repos out of workspace (breaks current setup)

PR fix notes

PR #69925: perf(plugins): prefer native jiti for bundled plugin dist modules

Description (problem / solution / changelog)

Summary

cc @gumadeiras

Full disclosure: I'm not a Node developer (I mainly work in Go / infra), and AI assistance was used to help me navigate this codebase, identify the root cause, and validate the change. I understand what the code does and verified it myself though.

  • Problem: resolvePluginLoaderJitiTryNative in sdk-alias.ts unconditionally returns false for bundled plugin dist paths, forcing every getCachedPluginJitiLoader caller through jiti's slow transform pipeline — even for pre-built .js/.mjs/.cjs files that Node can load natively.
  • Why it matters: Users are reporting 17-42s openclaw status times. This is one of the remaining contributors after #69786 landed the nodeRequire fast-path fix for loadBundledEntryModuleSync.
  • What changed: One line — the early-return false now delegates to shouldPreferNativeJiti(modulePath), which already correctly gates on platform (!win32, !bun) and file extension (.js/.mjs/.cjs/.json). Updated the corresponding test to match.
  • What did NOT change (scope boundary): No new dependencies, no plugin behavior, no Windows/Bun behavior (they still get false via shouldPreferNativeJiti), no changes outside src/plugins/.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Related #68282
  • Related #69462
  • Related #69758
  • Supersedes #68348 and #68324 (those fix individual call sites; this fixes the root cause)
  • This PR fixes a bug or regression

Root Cause (if applicable)

  • Root cause: isBundledPluginDistModulePath(modulePath) guard in resolvePluginLoaderJitiTryNative hard-returns false, bypassing the shouldPreferNativeJiti() check that already knows how to make the right platform-aware decision.
  • Missing detection / guardrail: The existing test asserted false as the expected value, so it was "working as designed" — just designed conservatively.
  • Contributing context (if known): The return false may have originally been a safety net before shouldPreferNativeJiti() had proper platform gating. Now that it handles win32 and bun correctly, the hard false is just unnecessary overhead.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/plugins/sdk-alias.test.ts
  • Scenario the test should lock in: Bundled plugin dist .js modules return shouldPreferNativeJiti() (true on Linux/macOS, false on Windows/Bun); .ts files still return false.
  • Why this is the smallest reliable guardrail: Tests the decision function directly without needing to spin up the full loader.
  • Existing test that already covers this (if any): Updated the existing "keeps bundled plugin dist modules on the aliased Jiti path" test — renamed it to reflect the new behavior.
  • If no new test is added, why not: Existing test was updated, not removed.

User-visible / Behavior Changes

None. CLI commands that load bundled plugins (status, health, etc.) should be faster on Linux/macOS. Output is identical.

Diagram (if applicable)

Before:
[bundled dist/extensions/*.js] -> resolvePluginLoaderJitiTryNative -> return false -> jiti parse (slow)

After:
[bundled dist/extensions/*.js] -> resolvePluginLoaderJitiTryNative -> shouldPreferNativeJiti() -> true on Linux/macOS -> jiti native delegation (fast)
                                                                                                -> false on Win32/Bun -> jiti parse (unchanged)

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: Ubuntu Linux (also affects macOS)
  • Runtime/container: Node v24.14.1
  • Model/provider: N/A
  • Integration/channel (if any): All bundled channels — most visible with Discord/Telegram
  • Relevant config (redacted): Default config with at least one channel plugin configured

Steps

  1. openclaw status --json on a fresh process (Linux or macOS)
  2. Observe startup time dominated by jiti transform for bundled plugin dist files

Expected

  • Bundled .js dist files load via native delegation, not jiti forced-parse

Actual

  • Before this fix: resolvePluginLoaderJitiTryNative returns false for all bundled dist paths, forcing jiti parse on every caller of getCachedPluginJitiLoader

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Profiling data from the detailed investigation in #68282 and #69462 shows jiti.normalizeAliases as the dominant hot frame in startup. The shouldPreferNativeJiti microbenchmark in the original analysis shows tryNative: false at 3347ms vs native at 2560ms per file — compounded across all bundled plugin dist modules this adds up to the ~1.5-4s savings this change provides (on top of the ~12.5s already saved by #69786).

Full validation suite run:

CheckResult
pnpm buildPass
pnpm check (typecheck + lint + policy guards)Pass — 0 errors, 0 warnings, 0 import cycles
pnpm test:contracts (channels + plugins)Pass — 96 files, 771 tests
pnpm test (full suite, 70 shards)Pass (9 pre-existing browser env failures, unrelated)
Import boundary checks (3 scripts)Pass — no violations
Extension memory profiler (Discord)Pass — 86 MB, status ok

Human Verification (required)

  • Verified scenarios: Full validation suite (pnpm build && pnpm check && pnpm test), contract tests, boundary checks, and extension memory profiler all pass. I have also been running openclaw with this patch for the last couple of days on a Linux host and it has produced a noticeable speed-up.
  • Edge cases checked: Windows behavior preserved (returns false via shouldPreferNativeJiti), .ts files still routed through jiti transform, Bun excluded via supportsNativeJitiRuntime().
  • What you did not verify: I don't have access to Windows or Bun to verify those paths at runtime (relied on existing test coverage and code inspection).

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.

N/A — first submission.

Compatibility / Migration

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

Risks and Mitigations

  • Risk: If any bundled dist/extensions/**/*.js module relies on jiti alias resolution that Node's native loader can't handle, it would fail on first attempt.
    • Mitigation: jiti's built-in fallback handles this — tryNative: true means "try native first, fall back to transform." The existing try/catch in loadBundledEntryModuleSync also provides a second safety net. This is the same pattern used by every other non-bundled dist path in the loader already.

[AI-assisted] — AI was used to navigate the codebase, identify the root cause, prepare the change, and validate it. I reviewed and understand all changes.

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • src/plugins/sdk-alias.test.ts (modified, +6/-2)
  • src/plugins/sdk-alias.ts (modified, +1/-1)

Code Example

[Shared libraries]:
  11869   34.7%   node (module loading)

[JavaScript]:
  11587   33.9%   55.0%   JS: *normalizeAliases jiti.cjs:1:141278
    397    1.2%    1.9%   Builtin: LoadIC
    256    0.7%    1.2%   Builtin: KeyedLoadIC_Megamorphic
    192    0.6%    0.9%   JS: *resolve node:path:1260:10
    138    0.4%    0.7%   JS: *string json5/lib/parse.js:570:12
    119    0.3%    0.6%   JS: *string json5/lib/parse.js:570:12

---

$ time openclaw status
real  0m42.038s
user  0m41.781s
sys   0m4.913s
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Summary

openclaw status takes ~42 seconds to complete, with 55% of JS execution time spent in jiti.normalizeAliases scanning and transpiling TypeScript files from workspace directories that are irrelevant to the CLI command.

Steps to reproduce

  1. Install OpenClaw 2026.4.15 globally via npm
  2. Add repos with TypeScript files to workspace (e.g. ~/.openclaw/workspace/repos/ with Next.js projects)
  3. Run time openclaw status
  4. Observe ~40-43 second completion time

Expected behavior

openclaw status should complete in a few seconds. The CLI should not scan workspace repos/ directories or node_modules/ for TypeScript transpilation during startup.

Actual behavior

  • openclaw status takes 42-43 seconds consistently
  • V8 profiling shows jiti.normalizeAliases consuming 33.9% of total CPU (55% of JS time)
  • Node native module loading takes 34.7% (module resolution overhead from 3,439 dist files + 566 node_modules packages)
  • json5.parse also consuming cycles on config file parsing
  • The CLI scans ~14,700 TS/TSX files across workspace, including:
    • repos/spq-plus/ — 8,813 TS files
    • repos/crm/ — 4,064 TS files
    • mission-control/ — 1,748 TS files
    • Plus .d.ts in node_modules/

Environment

  • OpenClaw version: 2026.4.15 (041266a)
  • OS: Ubuntu 24.04, Linux 6.8.0-110-generic (x64), 6 vCPU AMD EPYC, 11GB RAM
  • Install method: npm global
  • Node: v22.22.2
  • Package size: 1.2GB (/usr/lib/node_modules/openclaw/)
  • jiti version: 2.6.1

Profile evidence

V8 --prof output (34,208 ticks):

[Shared libraries]:
  11869   34.7%   node (module loading)

[JavaScript]:
  11587   33.9%   55.0%   JS: *normalizeAliases jiti.cjs:1:141278
    397    1.2%    1.9%   Builtin: LoadIC
    256    0.7%    1.2%   Builtin: KeyedLoadIC_Megamorphic
    192    0.6%    0.9%   JS: *resolve node:path:1260:10
    138    0.4%    0.7%   JS: *string json5/lib/parse.js:570:12
    119    0.3%    0.6%   JS: *string json5/lib/parse.js:570:12

Real-time measurements:

$ time openclaw status
real  0m42.038s
user  0m41.781s
sys   0m4.913s

Gateway API responds in <10ms, confirming the bottleneck is entirely in CLI startup/module resolution, not in the gateway daemon.

Impact and severity

  • Affected: All users with TypeScript repos in their workspace directory
  • Severity: High — 42s CLI makes any openclaw command painfully slow
  • Frequency: Always (every CLI invocation)
  • Consequence: Poor DX, delayed status checks, slow debugging workflows

Additional information

  • NODE_COMPILE_CACHE provides only marginal improvement (42s → 40s)
  • The core issue is jiti doing runtime TS→JS transpilation of files in workspace subdirectories that have nothing to do with OpenClaw configuration
  • Suggested fixes:
    1. Exclude repos/, node_modules/, and mission-control/ from jiti scan paths
    2. Support a .openclawignore or similar mechanism to exclude directories
    3. Consider lazy-loading jiti only when actually needed (e.g. config file parsing)
    4. Pre-compile config TS files at install time instead of runtime transpilation
  • Workaround: None practical without moving repos out of workspace (breaks current setup)

extent analysis

TL;DR

Exclude irrelevant directories from jiti scan paths to reduce TypeScript transpilation overhead and improve openclaw status performance.

Guidance

  1. Identify and exclude irrelevant directories: Determine which directories in the workspace are not necessary for openclaw configuration and exclude them from jiti scan paths, such as repos/, node_modules/, and mission-control/.
  2. Implement a ignore mechanism: Consider introducing a .openclawignore file or similar mechanism to allow users to specify directories to be excluded from jiti scanning.
  3. Optimize jiti usage: Explore lazy-loading jiti only when necessary, such as during config file parsing, to minimize unnecessary transpilation overhead.
  4. Pre-compile config files: Investigate pre-compiling config TS files at install time instead of relying on runtime transpilation to reduce the load on jiti.

Example

No specific code example is provided as the solution involves modifying the jiti configuration or implementing a new ignore mechanism, which requires more context about the openclaw and jiti implementations.

Notes

The provided suggestions are based on the profiling evidence and the described behavior of openclaw. The actual implementation details may vary depending on the internal workings of openclaw and jiti.

Recommendation

Apply workaround by excluding irrelevant directories from jiti scan paths, as this is the most direct approach to addressing the performance issue caused by unnecessary TypeScript transpilation. This can be done by introducing an ignore mechanism or optimizing jiti usage.

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 status should complete in a few seconds. The CLI should not scan workspace repos/ directories or node_modules/ for TypeScript transpilation during startup.

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 - ✅(Solved) Fix [Bug]: CLI startup extremely slow (~42s) due to jiti scanning workspace repos [1 pull requests, 1 comments, 2 participants]