April 17, 2026 โ€ข Version: 2026.4.12

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 queued indefinitely
  • 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:

  1. Base module exists: The chunk subagent-registry-CflSFWBm.js is correctly generated and placed in the dist folder.
  2. Internal import mismatch: Within subagent-registry-CflSFWBm.js, an internal dynamic import references subagent-registry.runtime.js using 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));
  1. Build artifact absent: The Rollup configuration generates hashed chunks for all other subagent-registry modules but fails to produce the subagent-registry.runtime.js entry point file.

The release notes for 2026.4.12 reference a similar fix for the install module:

“Fixed hashed chunk import in dist/install.runtime-*.js to 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

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

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 if exports field in package.json is misconfigured.
    Context: May appear if subagent-registry is also referenced via package exports.
  • Silent Task Queue Stalling
    Tasks remain in queued state without error propagation.
    Context: The downstream symptom of the module initialization failure.
  • 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))"

Evidence & Sources

This troubleshooting guide was automatically synthesized by the FixClaw Intelligence Pipeline from community discussions.