April 17, 2026 • 版本: 2026.4.12

subagent-registry.runtime.js 缺失于 dist — 子代理任务静默保持排队状态

一个带哈希的构建块引用了一个不存在的静态运行时文件,导致所有带有 'subagent' 运行时的 run_task 调用静默失败,而不抛出错误。

🔍 症状

启动警告

每次 OpenClaw 网关启动时,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

运行时行为

通过 webhooks 插件使用 runtime: "subagent" 调用 run_task 时:

POST /api/v1/flows/{flowId}/tasks
Content-Type: application/json

{
  "action": "run_task",
  "runtime": "subagent",
  "taskType": "data-process",
  "params": { ... }
}

任务在数据库中创建,但表现出以下特征:

  • 状态:无限期保持在 queued
  • deliveryStatus:卡在 pending
  • 错误可见性:不会向 API 调用者暴露任何错误
  • 调度器日志:没有记录到任务的调度尝试

dist 目录检查

列出 dist 文件夹显示不对称情况:

$ 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

🧠 根因分析

构建系统不一致

问题源于 Rollup 构建配置中不完整的 chunk 哈希迁移。具体如下:

  1. 基础模块存在:Chunk subagent-registry-CflSFWBm.js 被正确生成并放置在 dist 文件夹中。
  2. 内部导入不匹配:subagent-registry-CflSFWBm.js 内部,一个内部动态导入使用静态的、未哈希的文件名引用 subagent-registry.runtime.js
// 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. 构建产物缺失:Rollup 配置为所有其他 subagent-registry 模块生成了哈希 chunk,但未能生成 subagent-registry.runtime.js 入口点文件。

之前的相关修复(已应用的部分解决方案)

2026.4.12 的发布说明引用了对 install 模块的类似修复:

“修复了 dist/install.runtime-*.js 中的哈希 chunk 导入,使其引用正确的哈希文件名而不是静态路径。”

此修复已应用于 install.runtime,但 subagent-registry 的相同模式被忽略了。

故障序列

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

架构影响

SubagentRuntime 类负责调度具有 runtime: "subagent" 的任务。当其初始化静默失败时,调度器的守卫检查会检查有效的运行时实例并短路,导致任务在队列中停留,而不会向调用者传播任何错误。

🛠️ 逐步修复

选项 A:修补构建配置(推荐用于包维护者)

文件: rollup.config.mjs(或等效的 Rollup 配置)

之前:

export default {
  output: {
    chunkFileNames: '[name]-[hash].js',
    entryFileNames: '[name]-[hash].js',
    // ...
  }
};

之后:

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`;
      }
    }
  }
};

源文件中的辅助修复: src/subagent-registry.ts

将静态导入路径替换为正确的动态 chunk 解析:

// 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

选项 B:最终用户热修复(临时)

如果您无法等待官方补丁,请创建缺失的运行时存根:

步骤 1: 通过检查哈希 chunk 来识别正确的运行时导出:

$ head -100 /opt/homebrew/lib/node_modules/openclaw/dist/subagent-registry-CflSFWBm.js

步骤 2: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';

步骤 3: 重启网关:

$ openclaw gateway restart

警告: 这是一个临时解决方案,在下次 npm update 时会被覆盖。


选项 C:降级到之前的版本

如果生产环境需要立即修复:

$ npm install -g [email protected]
$ openclaw gateway restart

验证之前版本中的文件是否存在:

$ ls /usr/local/lib/node_modules/openclaw/dist/subagent-registry.runtime.js
# Should output the file path if it exists

🧪 验证

步骤 1:验证启动日志是否干净

重启网关并检查模块警告是否消失:

$ 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)

步骤 2:确认运行时文件存在

$ ls -la $(npm root -g)/openclaw/dist/subagent-registry.runtime.js
# Expected: File exists with non-zero size

步骤 3:测试 Subagent 任务调度

创建一个测试 flow 并调度一个 subagent 任务:

# 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

修复后的预期结果:

Attempt 1: queued
Attempt 2: running
Attempt 3: completed

步骤 4:验证数据库任务状态转换

直接查询数据库以确认状态机进展:

$ 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;
"

预期: status 应在 30 秒内从 queuedrunningcompleted 进展。

步骤 5:检查调度器日志

$ grep -E "(dispatch|subagent)" /var/log/openclaw/dispatcher.log | tail -20

预期: 显示 dispatching task {taskId} to subagent runtime 的条目

⚠️ 常见陷阱

1. 非致命警告掩盖关键故障

陷阱: 警告以 warn 级别记录,不会中止启动,导致运维人员在日志中忽略它。

缓解措施: 在健康检查时始终检查 [warn] 条目:

# Add to monitoring
$ grep "\[warn\].*subagent" /var/log/openclaw/gateway.log && echo "CRITICAL: Subagent module load failed"

2. 高容量系统中任务静默排队

陷阱: 在有许多排队任务的生产环境中,持续的 queued/pending 状态可能看起来是正常的。

缓解措施: 对处于 queued 状态超过阈值的任务发出警报:

-- 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 安装路径差异

陷阱: Apple Silicon 上的 Homebrew 使用 /opt/homebrew,而 Intel Mac 使用 /usr/local。文档可能引用了错误的路径。

缓解措施: 使用 npm root -g 确定正确的路径:

$ echo $(npm root -g)
/opt/homebrew/lib/node_modules  # Apple Silicon
# OR
/usr/local/lib/node_modules     # Intel

4. Docker 容器层缓存

陷阱: 如果从 openclaw 构建自定义 Docker 镜像,损坏的 dist 文件夹可能被缓存。

缓解措施: 清除构建缓存或使用多阶段构建:

RUN npm cache clean --force && \
    npm install -g openclaw@latest

5. CLI 和运行时之间的版本不匹配

陷阱: 从与 Node.js 中导入的包不同的安装运行 openclaw gateway

缓解措施: 验证一致性:

$ 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 模块解析严格性

缓解措施: Node.js v25+ 严格强制执行 ESM 模块解析。带有不正确扩展名或缺少 .js 扩展名的相对导入将失败。

缓解措施: 确保在使用 ESM 时所有导入都包含 .js 扩展:

// Correct
import { Something } from './something.js';

// Incorrect (will fail in Node.js ESM)
import { Something } from './something';

🔗 相关错误

上下文相关的错误

  • ERR_MODULE_NOT_FOUND
    当导入的文件路径在磁盘上不存在时,Node.js 模块解析失败。
    上下文:损坏的导入语句发出的主要错误。
  • ERR_PACKAGE_PATH_NOT_EXPORTED
    如果 package.json 中的 exports 字段配置错误,则会出现相关的模块解析错误。
    上下文:如果 subagent-registry 也通过包导出引用,则可能会出现。
  • 静默任务队列停滞
    任务保持在 queued 状态而不会传播错误。
    上下文:模块初始化失败的直接下游症状。

历史相关问题

  • GH Issue #4521 — install.runtime.js missing from dist
    与 install 模块的类似哈希 chunk 导入问题,已在 v2026.4.12 中修复。相同的模式未应用于 subagent-registry。
    解决方案:部分修复 — 仅应用于 install 模块。
  • GH Issue #3892 — Dynamic imports failing with Rollup chunking
    有关在哈希 chunk 之间保持导入一致性的通用 Rollup 配置指南。
  • GH Issue #5107 — Subagent dispatcher bypasses error handling
    报告 subagent 运行时故障被捕获并记录但未传播,导致静默任务失败。

诊断命令参考

# 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))"

依据与来源

本故障排除指南由 FixClaw 智能管线从社区讨论中自动合成。