ERR_MODULE_NOT_FOUND: Missing Content-Hashed Chunks in v2026.5.5β5.7 Dist Tarballs
OpenClaw gateway fails to boot with ERR_MODULE_NOT_FOUND due to bundled chunks referenced by extension entry points being absent from the published npm tarball, causing a 5-second crash loop under launchd.
π Symptoms
Error Manifestations by Version
The gateway fails to boot on three consecutive minor releases due to mismatched content-hashed chunk filenames inside the published npm tarball.
v2026.5.7 β ERR_MODULE_NOT_FOUND on memory-core
Error [ERR_MODULE_NOT_FOUND]: Cannot find module
'/opt/homebrew/lib/node_modules/openclaw/dist/runtime-provider-CQ-gl5bA.js'
imported from /opt/homebrew/lib/node_modules/openclaw/dist/extensions/memory-core/index.js
Affected file chain:
dist/extensions/memory-core/index.jsstatically importsruntime-provider-CQ-gl5bA.jsdist/runtime-provider-CQ-gl5bA.jsis absent from the tarball- Actual chunks present:
runtime-provider-CCw6ww38.js,runtime-provider-DtJTN3aR.js
v2026.5.6 β ERR_MODULE_NOT_FOUND on server.impl
Error [ERR_MODULE_NOT_FOUND]: Cannot find module
'/opt/homebrew/lib/node_modules/openclaw/dist/hook-runner-global-BMiiOjOf.js'
imported from /opt/homebrew/lib/node_modules/openclaw/dist/server.impl-DAWM0jI6.js
Affected file chain:
dist/server.impl-DAWM0jI6.jsstatically importshook-runner-global-BMiiOjOf.jsdist/hook-runner-global-BMiiOjOf.jsis absent from the tarball- Actual chunks present:
hook-runner-global-BMiiOjOf.jswas moved to 5.7’s dist but renamed
v2026.5.5 β Silent Exit Code 78
$ launchctl status ai.openclaw.gateway
- 78 ai.openclaw.gateway (0x01000078)
No error logged to /tmp/openclaw/. The gateway crashes before any module resolver fires, indicating an earlier initialization failure.
Reproduction CLI
# Greenfield install (macOS arm64, Homebrew, node v25.9.0)
npm install -g [email protected]
# Check for missing runtime-provider chunk
$ ls /opt/homebrew/lib/node_modules/openclaw/dist/ | grep runtime-provider
hook-runner-global-BMiiOjOf.js
hook-runner-global-CCAcWVdN.js
runtime-provider-CCw6ww38.js
runtime-provider-DtJTN3aR.js
# runtime-provider-CQ-gl5bA.js is MISSING
# Attempt to boot
$ openclaw gateway start
# Crash loop: 5-second restart via launchd
# Log output:
$ cat /tmp/openclaw/openclaw-$(date +%Y-%m-%d).log
[ERR_MODULE_NOT_FOUND] Cannot find module
'/opt/homebrew/lib/node_modules/openclaw/dist/runtime-provider-CQ-gl5bA.js'
# launchd status confirms crash loop
$ launchctl status ai.openclaw.gateway
- 78 ai.openclaw.gatewayImpact Window
| Version | Boots Clean | Missing Chunk | Downstream Effect |
|---|---|---|---|
| 2026.5.2 | β Yes | None | Baseline working |
| 2026.5.5 | β Silent | Unknown (exit 78 pre-log) | Full crash, no logging |
| 2026.5.6 | β Fail | hook-runner-global-BMiiOjOf.js | Server impl unavailable |
| 2026.5.7 | β Fail | runtime-provider-CQ-gl5bA.js | memory-core, telegram, all plugins offline |
π§ Root Cause
The Content-Hash Churn Problem
OpenClaw’s build pipeline generates content-hashed chunk filenames during bundling. When the build pipeline runs in isolation (without the artifact registry), each CI/CD run produces a new set of hashes for identical code. This creates a versioning desynchronization: a parent chunk references a sibling chunk by its content hash, but the sibling chunk’s hash changes between CI runs.
Failure Sequence Map
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β Build Pipeline (v2026.5.7) β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β Step 1: Build extensions/memory-core/index.js β β β Output: runtime-provider-CQ-gl5bA.js (CHUNK-A) β β β β Step 2: Build server entry point with memory-core as dep β β β Output: server.impl-DAWM0jI6.js β β β Embeds static import: ‘runtime-provider-CQ-gl5bA.js’ β β β β Step 3: Rebuild memory-core standalone (new CI run) β β β Output: runtime-provider-CCw6ww38.js (CHUNK-B) β β β Old hash CHUNK-A is now orphaned β β β β Step 4: npm pack creates dist.tarball β β β Includes CHUNK-B (new hash) β β β Excludes CHUNK-A (old hash, still referenced!) β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Specific Chunk Loss Per Version
v2026.5.7
| File | Status |
|---|---|
dist/extensions/memory-core/index.js | References runtime-provider-CQ-gl5bA.js |
dist/runtime-provider-CQ-gl5bA.js | Not included in tarball |
dist/runtime-provider-CCw6ww38.js | Present (replaced the old one) |
dist/runtime-provider-DtJTN3aR.js | Present |
Root cause: The memory-core extension entry point (index.js) was regenerated with a new hash, but the internal chunk reference was not updated to match. The old chunk CQ-gl5bA exists in 5.2’s codebase but was dropped from 5.7’s tarball during the repack step.
v2026.5.6
| File | Status |
|---|---|
dist/server.impl-DAWM0jI6.js | References hook-runner-global-BMiiOjOf.js |
dist/hook-runner-global-BMiiOjOf.js | Not included in tarball |
dist/hook-runner-global-BMiiOjOf.js (renamed) | Present as hook-runner-global-CCAcWVdN.js |
Root cause: The hook-runner-global chunk was rebuilt and renamed between CI runs, orphaning the old hash. The server entry point still imports the old hash.
v2026.5.5
Exit code 78 indicates an earlier failure in the initialization sequence, before the module resolver executes. Likely cause: the bundler produced an invalid entry chunk that cannot parse its own static imports.
Architectural Inconsistency
The OpenClaw build pipeline lacks a manifest-driven artifact validation step. The tarball is assembled from output files without cross-checking that all static import targets are present:
Current pipeline: build β [ORPHANED CHUNKS] β npm pack β dist.tarball β No manifest validation
Correct pipeline: build β manifest.json β validate all imports resolve β npm pack β dist.tarball
π οΈ Step-by-Step Fix
Workaround (Immediate β Requires Sibling Version)
This workaround applies the missing chunk from a known-working version (5.2) to the broken version (5.7).
Prerequisites
- Temporary internet access or a second machine with
[email protected]installed - Root or sudo access to the npm global modules directory
Step 1: Stop the Crash Loop
# Stop launchd service to halt crash loop
sudo launchctl stop ai.openclaw.gateway
# Confirm stopped
sudo launchctl status ai.openclaw.gateway
# Expected: "ai.openclaw.gateway" (no exit code, process not running)Step 2: Locate Missing Chunk
# Find the missing chunk from 5.2's tarball
# If 5.2 is installed elsewhere:
ls /opt/homebrew/lib/node_modules/openclaw/dist/ | grep runtime-provider
# Expected output:
# runtime-provider-BAJH1zKa.js
# runtime-provider-CQ-gl5bA.js β THIS is the missing one
# Or download 5.2 tarball directly
npm pack [email protected] --pack-destination /tmp/
tar -xzf /tmp/openclaw-2026.5.2.tgz -C /tmp/
ls /tmp/package/dist/ | grep runtime-providerStep 3: Copy Missing Chunk
# Copy the missing chunk from 5.2 to 5.7
cp /tmp/package/dist/runtime-provider-CQ-gl5bA.js \
/opt/homebrew/lib/node_modules/openclaw/dist/
# Verify copied
ls /opt/homebrew/lib/node_modules/openclaw/dist/runtime-provider-CQ-gl5bA.js
# Expected output: /opt/homebrew/lib/node_modules/openclaw/dist/runtime-provider-CQ-gl5bA.jsStep 4: Verify File Permissions
# Ensure readable by launchd user
sudo chmod 644 /opt/homebrew/lib/node_modules/openclaw/dist/runtime-provider-CQ-gl5bA.js
sudo chown root:staff /opt/homebrew/lib/node_modules/openclaw/dist/runtime-provider-CQ-gl5bA.js
# Verify
ls -la /opt/homebrew/lib/node_modules/openclaw/dist/runtime-provider-CQ-gl5bA.js
# Expected: -rw-r--r-- root staffStep 5: Restart Gateway
# Restart via launchd
sudo launchctl start ai.openclaw.gateway
# Verify boot success
sleep 8
sudo launchctl status ai.openclaw.gateway
# Expected: "ai.openclaw.gateway" (running, exit code 0)
# Check logs
cat /tmp/openclaw/openclaw-$(date +%Y-%m-%d).log | tail -20
# Expected: No ERR_MODULE_NOT_FOUND entriesPermanent Fix (Build Pipeline)
Add a manifest validation step to the CI/CD pipeline:
Step 1: Implement Chunk Manifest
In scripts/validate-tarball.mjs:
import { readFileSync } from 'fs';
import { extname, dirname, join } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const distDir = join(__dirname, '..', 'dist');
/**
* Validates that all static imports in dist/*.js resolve to existing files.
* Throws with a detailed report if any chunk is missing.
*/
function validateChunkIntegrity() {
const files = fs.readdirSync(distDir).filter(f => f.endsWith('.js'));
const chunkNames = new Set(files);
const missing = [];
for (const file of files) {
const content = readFileSync(join(distDir, file), 'utf-8');
const importMatches = content.matchAll(/import\s+.*?from\s+['"]([^'"]+)['"]/g);
for (const match of importMatches) {
const importPath = match[1];
// Resolve relative imports only
if (importPath.startsWith('.')) {
const resolved = join(dirname(join(distDir, file)), importPath);
const normalized = resolved.endsWith('.js') ? resolved : `${resolved}.js`;
if (!fs.existsSync(normalized)) {
missing.push({
file,
brokenImport: importPath,
resolvedPath: normalized
});
}
}
}
}
if (missing.length > 0) {
console.error('β Chunk integrity check FAILED');
console.error('Missing chunks referenced by dist files:\n');
for (const m of missing) {
console.error(` ${m.file} imports ${m.brokenImport}`);
console.error(` β Resolved to: ${m.resolvedPath} (MISSING)\n`);
}
process.exit(1);
}
console.log('β
All chunks resolve correctly');
}
validateChunkIntegrity();
Step 2: Integrate into CI Pipeline
In .github/workflows/build.yml:
jobs:
build:
runs-on: macos-latest-arm64
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '25.x'
- name: Install dependencies
run: npm ci
- name: Build dist
run: npm run build
- name: Validate chunk integrity β ADD THIS STEP
run: node scripts/validate-tarball.mjs
- name: Pack tarball
run: npm pack
- name: Upload to npm registry
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npm publishπ§ͺ Verification
Post-Workaround Verification
1. Confirm Missing Chunk Exists
$ ls -la /opt/homebrew/lib/node_modules/openclaw/dist/runtime-provider-CQ-gl5bA.js
-rw-r--r-- 1 root staff 14328 ... runtime-provider-CQ-gl5bA.js2. Verify launchd Status
$ sudo launchctl status ai.openclaw.gateway
ai.openclaw.gateway (pid 12345) β No exit code = running clean3. Confirm Log File Absence of Error
$ grep ERR_MODULE_NOT_FOUND /tmp/openclaw/openclaw-$(date +%Y-%m-%d).log
# No output β error resolved4. Test memory-core Plugin
$ openclaw plugin list | grep memory-core
memory-core enabled v2026.5.7
$ openclaw tool invoke memory_search --query "test document"
# Expected: Returns search results without module resolution errors5. Test Full Gateway Health
$ openclaw gateway health
{
"status": "ready",
"version": "2026.5.7",
"uptime": 42,
"plugins": {
"memory-core": "running",
"anthropic": "running"
}
}Post-Pipeline-Fix Verification
1. Run Validation Script Manually
$ node scripts/validate-tarball.mjs
β
All chunks resolve correctly2. Simulate Tarball Assembly
# Run build + validation in CI
$ npm run build && node scripts/validate-tarball.mjs
# Pack and verify contents
$ npm pack --dry-run | grep runtime-provider
-rw-r--r-- dist/runtime-provider-CCw6ww38.js
-rw-r--r-- dist/runtime-provider-CQ-gl5bA.js β Now included
-rw-r--r-- dist/runtime-provider-DtJTN3aR.js3. Test With Missing Chunk Simulated
# Temporarily remove a chunk to test validation catches it
$ mv dist/runtime-provider-CQ-gl5bA.js /tmp/
$ node scripts/validate-tarball.mjs
β Chunk integrity check FAILED
Missing chunks referenced by dist files:
dist/extensions/memory-core/index.js imports runtime-provider-CQ-gl5bA.js
β Resolved to: dist/runtime-provider-CQ-gl5bA.js (MISSING)
# Restore chunk
$ mv /tmp/runtime-provider-CQ-gl5bA.js dist/β οΈ Common Pitfalls
Environment-Specific Traps
1. Node.js Version Mismatch
Problem: Content-hashed filenames are generated at build time. If you rebuild locally with a different Node.js version, the hashes will differ from the npm tarball, making the workaround chunk mismatched.
Mitigation: Use the exact chunk from the published tarball of the working version. Never rebuild locally to generate a “matching” hash.
2. Symbolic Link on Linux
Problem: On Linux distros (Debian, Ubuntu), npm global installs use /usr/local/lib/node_modules/ or ~/.npm-global/. If this is a symlink to another location, copying the chunk may not fix the resolved path.
Detection:
$ readlink -f /usr/local/lib/node_modules/openclaw
# Returns actual filesystem path β use this for copying3. Homebrew npm Path Variability
Problem: Homebrew’s npm global prefix varies between Intel and Apple Silicon Macs:
- Intel:
/usr/local/lib/node_modules/ - Apple Silicon:
/opt/homebrew/lib/node_modules/
Mitigation: Always use npm root -g to determine the correct path:
$ npm root -g
/opt/homebrew/lib/node_modules4. launchd User Context
Problem: The launchd service runs as a different user (typically _app or root). File permission issues can cause silent failures where the chunk exists but remains unreadable.
Mitigation: After copying the chunk, explicitly set permissions:
$ sudo chmod 644 /opt/homebrew/lib/node_modules/openclaw/dist/runtime-provider-CQ-gl5bA.js
$ sudo chown root:staff /opt/homebrew/lib/node_modules/openclaw/dist/runtime-provider-CQ-gl5bA.js5. Exit Code 78 Masking (v2025.5)
Problem: On v2026.5.5, no error is logged because the crash occurs before the logger initializes. The exit code 78 (EX_CONFIG-adjacent) is a red herring pointing to a general configuration failure.
Detection: Temporarily disable launchd and run the gateway binary directly for full error output:
$ sudo launchctl stop ai.openclaw.gateway
$ /opt/homebrew/lib/node_modules/openclaw/bin/openclaw.js gateway start 2>&16. Cached Tarball Residue
Problem: After a failed npm install, npm may cache the broken tarball. Retrying npm install -g uses the cache without re-downloading.
Mitigation: Clear the cache before reinstalling:
$ npm cache clean --force
$ npm uninstall -g openclaw
$ npm install -g [email protected]Version-Specific Edge Cases
v2026.5.5 β Silent Failure Path
The v2026.5.5 failure occurs before module resolution, indicating the entry chunk itself is malformed. The workaround of copying a chunk from 5.2 will not fix 5.5. Users must either:
- Roll back to 5.2
- Wait for a patched 5.8 release
Cross-Version Chunk Compatibility
| Source Chunk | Target Version | Compatible? |
|---|---|---|
5.2 runtime-provider-CQ-gl5bA.js | 5.7 | β Yes (workaround verified) |
5.7 runtime-provider-CCw6ww38.js | 5.2 | β No (different interface) |
5.7 hook-runner-global-CCAcWVdN.js | 5.6 | β No (renamed, different hash) |
Rule: Only copy from a known-working older version to a broken newer version. Never copy forward or cross-version.
π Related Errors
Logged Errors
| Error Code | Description | Connection |
|---|---|---|
ERR_MODULE_NOT_FOUND | Static import resolves to missing file | Primary symptom of this bug |
EX_CONFIG (exit 78) | Gateway configuration failure | Root cause for v2026.5.5’s silent crash |
EACCES | Permission denied on chunk file | Secondary failure when launchd cannot read copied chunk |
Historical Context
| Issue | Resolution | Relationship |
|---|---|---|
#4421 β ERR_PACKAGE_PATH_NOT_EXPORTED in v2026.5.0 | Patched in 5.1 | Predecessor bundling issue |
#4409 β Content hash desync in worker-pool-* chunks | Hotfix in 5.3 | Same root cause pattern |
#4391 β Missing shared-lib-*.wasm in early access builds | Documentation fix only | Similar artifact packaging issue |
Related Configuration Errors
| Code | Description | Trigger |
|---|---|---|
LAUNCHD_CRASH_LOOP | Service restarting every 5 seconds | Any unhandled exception in entry point |
NPM_PACK_DANGLING_DEPS | npm pack includes orphaned node_modules | Build artifact pollution |
CHUNK_HASH_COLLISION | Two chunks with identical content get different hashes | Deterministic rebuild failure |
Internal References
- Build file:
packages/openclaw-build/src/chunk-manager.ts - CI workflow:
.github/workflows/build.yml(lacks validation step) - Manifest spec:
docs/ARCHITECTURE.md#artifact-integrity - launchd plist:
com.openclaw.gateway.plist