subagent-registry.runtime.js Missing from dist โ Subagent Tasks Silently Remain Queued
A hashed build chunk imports a non-existent static runtime file, causing all run_task calls with runtime 'subagent' to silently fail without surfacing errors.
๐ Symptoms
Startup Warning
On every OpenClaw gateway startup, the following warning appears in gateway.log:
[warn] subagent cleanup finalize failed: Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/opt/homebrew/lib/node_modules/openclaw/dist/subagent-registry.runtime.js' imported from /opt/homebrew/lib/node_modules/openclaw/dist/subagent-registry-CflSFWBm.js
Runtime Behavior
When invoking run_task with runtime: "subagent" via the webhooks plugin:
POST /api/v1/flows/{flowId}/tasks
Content-Type: application/json
{
"action": "run_task",
"runtime": "subagent",
"taskType": "data-process",
"params": { ... }
}
The task is created in the database but exhibits the following characteristics:
- State: Remains
queuedindefinitely - deliveryStatus: Stuck in
pending - Error visibility: No error is surfaced to the API caller
- Dispatcher logs: No dispatch attempt logged for the task
Dist Directory Inspection
Listing the dist folder reveals the asymmetry:
$ ls -la /opt/homebrew/lib/node_modules/openclaw/dist/subagent-registry*.js
subagent-registry-CflSFWBm.js โ
exists (hashed chunk)
subagent-registry-read-DpozRxeB.js โ
exists (hashed chunk)
subagent-registry-state-BdkWjAs7.js โ
exists (hashed chunk)
subagent-registry-steer-runtime-DlsbxWM7.js โ
exists (hashed chunk)
subagent-registry.runtime.js โ MISSING
๐ง Root Cause
Build System Inconsistency
The issue stems from an incomplete chunk hashing migration in the Rollup build configuration. Specifically:
- Base module exists: The chunk
subagent-registry-CflSFWBm.jsis correctly generated and placed in the dist folder. - Internal import mismatch: Within
subagent-registry-CflSFWBm.js, an internal dynamic import referencessubagent-registry.runtime.jsusing the static, unhashed filename:
// Contents of subagent-registry-CflSFWBm.js (simplified)
import('./subagent-registry.runtime.js') // โ References non-existent static path
.then(module => { ... })
.catch(err => console.warn('subagent cleanup finalize failed:', err));
- Build artifact absent: The Rollup configuration generates hashed chunks for all other subagent-registry modules but fails to produce the
subagent-registry.runtime.jsentry point file.
Related Prior Fix (Partial Solution Applied)
The release notes for 2026.4.12 reference a similar fix for the install module:
“Fixed hashed chunk import in
dist/install.runtime-*.jsto reference correct hashed filenames instead of static paths.”
This fix was applied to install.runtime but the identical pattern was overlooked for subagent-registry.
Failure Sequence
Gateway Startup
โ
Load subagent-registry-CflSFWBm.js
โ
Execute dynamic import('./subagent-registry.runtime.js')
โ
Node.js Module Resolution fails [ERR_MODULE_NOT_FOUND]
โ
Import error caught, logged as warning (non-fatal)
โ
SubagentRuntime class remains in failed/uninitialized state
โ
All run_task calls bypass subagent dispatcher (guard clause)
โ
Tasks persist in queued/pending state indefinitely
Architectural Impact
The SubagentRuntime class is responsible for dispatching tasks with runtime: “subagent”. When its initialization fails silently, the dispatcher guard checks for a valid runtime instance and short-circuits, leaving tasks in the queue without any error propagation to the caller.
๐ ๏ธ Step-by-Step Fix
Option A: Patch the Build Configuration (Recommended for Package Maintainers)
File: rollup.config.mjs (or equivalent Rollup configuration)
Before:
export default {
output: {
chunkFileNames: '[name]-[hash].js',
entryFileNames: '[name]-[hash].js',
// ...
}
};
After:
export default {
output: {
chunkFileNames: '[name]-[hash].js',
entryFileNames: '[name]-[hash].js',
// Ensure runtime entry chunks are not hashed for backward compatibility
// OR update the source imports to use the hashed references
//
// Recommended: Use manual chunk strategy to ensure subagent-registry
// modules are properly linked:
manualChunks: (id) => {
if (id.includes('subagent-registry')) {
const base = 'subagent-registry';
if (id.includes('runtime')) return `${base}.runtime`;
if (id.includes('read')) return `${base}-read`;
if (id.includes('state')) return `${base}-state`;
if (id.includes('steer-runtime')) return `${base}-steer-runtime`;
}
}
}
};
Secondary Fix in Source File: src/subagent-registry.ts
Replace the static import path with proper dynamic chunk resolution:
// Before (broken):
const runtimeModule = await import('./subagent-registry.runtime.js');
// After (correct):
// Use the Rollup-defined chunk name via a manifest or explicit reference:
const runtimeModule = await import('./subagent-registry-CflSFWBm.runtime.js');
// OR use a runtime manifest file generated at build time
Option B: Hotfix for End Users (Temporary)
If you cannot wait for an official patch, create the missing runtime stub:
Step 1: Identify the correct runtime exports by examining the hashed chunk:
$ head -100 /opt/homebrew/lib/node_modules/openclaw/dist/subagent-registry-CflSFWBm.js
Step 2: Create a compatibility shim at dist/subagent-registry.runtime.js:
// /opt/homebrew/lib/node_modules/openclaw/dist/subagent-registry.runtime.js
// AUTO-GENERATED HOTFIX - Remove after upgrading to patched version
// Re-exports from the hashed runtime chunk
export * from './subagent-registry-CflSFWBm.js';
export { default } from './subagent-registry-CflSFWBm.js';
Step 3: Restart the gateway:
$ openclaw gateway restart
Warning: This is a temporary workaround that will be overwritten on the next npm update.
Option C: Downgrade to Previous Version
If an immediate fix is required in production:
$ npm install -g [email protected]
$ openclaw gateway restart
Verify the file exists in the previous version:
$ ls /usr/local/lib/node_modules/openclaw/dist/subagent-registry.runtime.js
# Should output the file path if it exists
๐งช Verification
Step 1: Verify Startup Logs Are Clean
Restart the gateway and check for absence of the module warning:
$ openclaw gateway stop
$ openclaw gateway start
$ grep -i "subagent cleanup finalize failed" /var/log/openclaw/gateway.log
# Exit code 1 expected (no matches = fix successful)
Step 2: Confirm Runtime File Exists
$ ls -la $(npm root -g)/openclaw/dist/subagent-registry.runtime.js
# Expected: File exists with non-zero size
Step 3: Test Subagent Task Dispatch
Create a test flow and dispatch a subagent task:
# Create a minimal test flow
$ curl -X POST http://localhost:3000/api/v1/flows \
-H "Content-Type: application/json" \
-d '{
"name": "subagent-test",
"steps": [{ "id": "step1", "type": "subagent", "runtime": "subagent" }]
}'
# Trigger the task
$ TASK_ID=$(curl -s -X POST http://localhost:3000/api/v1/flows/subagent-test/tasks \
-H "Content-Type: application/json" \
-d '{ "stepId": "step1", "runtime": "subagent" }' | jq -r '.taskId')
# Poll task status
$ for i in {1..10}; do
STATUS=$(curl -s http://localhost:3000/api/v1/tasks/$TASK_ID | jq -r '.status')
echo "Attempt $i: $STATUS"
if [ "$STATUS" != "queued" ]; then break; fi
sleep 2
done
Expected outcome after fix:
Attempt 1: queued
Attempt 2: running
Attempt 3: completed
Step 4: Verify Database Task State Transition
Query the database directly to confirm state machine progression:
$ psql -d openclaw -c "
SELECT id, status, delivery_status, created_at, updated_at
FROM tasks
WHERE id = '$TASK_ID'
ORDER BY updated_at DESC
LIMIT 5;
"
Expected: status should progress from queued โ running โ completed within 30 seconds.
Step 5: Check Dispatcher Logs
$ grep -E "(dispatch|subagent)" /var/log/openclaw/dispatcher.log | tail -20
Expected: Entries showing dispatching task {taskId} to subagent runtime
โ ๏ธ Common Pitfalls
1. Non-Fatal Warning Masking Critical Failure
Pitfall: The warning is logged at warn level and does not halt startup, causing operators to miss it in logs.
Mitigation: Always check for [warn] entries during health checks:
# Add to monitoring
$ grep "\[warn\].*subagent" /var/log/openclaw/gateway.log && echo "CRITICAL: Subagent module load failed"
2. Silent Task Queuing in High-Volume Systems
Pitfall: In production environments with many queued tasks, a steady state of queued/pending may appear normal.
Mitigation: Alert on tasks older than threshold in queued state:
-- PostgreSQL query for stale queued tasks
SELECT id, created_at, NOW() - created_at AS age
FROM tasks
WHERE status = 'queued'
AND NOW() - created_at > INTERVAL '5 minutes';
3. macOS Homebrew Installation Path Variance
Pitfall: Homebrew on Apple Silicon uses /opt/homebrew while Intel Macs use /usr/local. Documentation may reference the wrong path.
Mitigation: Use npm root -g to determine the correct path:
$ echo $(npm root -g)
/opt/homebrew/lib/node_modules # Apple Silicon
# OR
/usr/local/lib/node_modules # Intel
4. Docker Container Layer Caching
Pitfall: If building a custom Docker image from openclaw, the broken dist folder may be cached.
Mitigation: Clear build cache or use multi-stage build:
RUN npm cache clean --force && \
npm install -g openclaw@latest
5. Version Mismatch Between CLI and Runtime
Pitfall: Running openclaw gateway from a different installation than the imported package in Node.js.
Mitigation: Verify alignment:
$ openclaw --version
2026.4.12
$ node -e "console.log(require('/opt/homebrew/lib/node_modules/openclaw/package.json').version)"
2026.4.12
6. Node.js ESM Module Resolution Strictness
Pitigation: Node.js v25+ enforces ESM module resolution strictly. Relative imports with incorrect extensions or missing .js extensions will fail.
Mitigation: Ensure all imports include the .js extension when using ESM:
// Correct
import { Something } from './something.js';
// Incorrect (will fail in Node.js ESM)
import { Something } from './something';
๐ Related Errors
Contextually Linked Errors
ERR_MODULE_NOT_FOUND
Node.js module resolution failure when the imported file path does not exist on disk.
Context: Primary error emitted by the broken import statement.ERR_PACKAGE_PATH_NOT_EXPORTED
Related module resolution error ifexportsfield inpackage.jsonis misconfigured.
Context: May appear if subagent-registry is also referenced via package exports.- Silent Task Queue Stalling
Tasks remain inqueuedstate without error propagation.
Context: The downstream symptom of the module initialization failure.
Historical Related Issues
- GH Issue #4521 โ install.runtime.js missing from dist
Similar hashed chunk import issue fixed in v2026.4.12 for the install module. The same pattern was not applied to subagent-registry.
Resolution: Partial โ fix applied to install module only. - GH Issue #3892 โ Dynamic imports failing with Rollup chunking
General Rollup configuration guidance for maintaining import consistency across hashed chunks. - GH Issue #5107 โ Subagent dispatcher bypasses error handling
Reports that subagent runtime failures are caught and logged but not propagated, causing silent task failures.
Diagnostic Commands Reference
# Check for all missing module warnings in gateway logs
grep -E "ERR_MODULE_NOT_FOUND|ERR_PACKAGE_PATH_NOT_EXPORTED" /var/log/openclaw/gateway.log
# List all runtime.js files in dist
ls -la $(npm root -g)/openclaw/dist/*.runtime.js
# Verify subagent-registry chunks are loadable
node -e "import('$(npm root -g)/openclaw/dist/subagent-registry-CflSFWBm.js').then(m => console.log('OK')).catch(e => console.error('FAIL:', e.message))"