记一次 OpenClaw 定时任务的诡异报错:Missing workspace template

凌晨改完代码准备睡觉,定时任务却悄悄翻车了。排查了一小时,发现是打包路径的一个经典坑。

背景

最近在用 OpenClaw 搭建自动化运营流程——定时发推、自动互动、掘金发文,全靠 cron job 驱动。OpenClaw 的 cron 任务可以跑在 isolated session(隔离会话)里,每个任务独立执行,互不干扰。

一切运行良好,直到今天早上,其中一个任务突然报错:

Error: Missing workspace template: AGENTS.md 
(/docs/reference/templates/AGENTS.md). 
Ensure docs/reference/templates are packaged.

关键信息:8 毫秒就失败了,说明不是执行过程中的问题,而是 session 启动就崩了。

诡怪的地方

同样是 isolated cron session,其他 4 个任务都正常——包括同类型的推文任务。而且这个任务前天还跑成功了,昨天就挂了。时好时坏,非常诡异。

排查过程

第一步:确认模板文件是否存在
ls -la /opt/homebrew/lib/node_modules/openclaw/docs/reference/templates/AGENTS.md

文件明明在。那为什么报找不到?

第二步:看源码怎么找模板

dist/workspace-*.js 里找到了关键逻辑:

const FALLBACK_TEMPLATE_DIR = path.resolve(
  path.dirname(fileURLToPath(import.meta.url)), 
  "../../docs/reference/templates"
);

// 候选路径列表
const candidates = [
  packageRoot ? path.join(packageRoot, "docs", "reference", "templates") : null,
  cwd ? path.resolve(cwd, "docs", "reference", "templates") : null,
  FALLBACK_TEMPLATE_DIR
].filter(Boolean);

三个候选路径,按优先级依次尝试:

  1. packageRoot — 从包的根目录找
  2. cwd — 从当前工作目录找
  3. FALLBACK_TEMPLATE_DIR — 用硬编码的相对路径兜底
第三步:发现根因

问题出在打包后的目录结构

openclaw/
├── dist/
│   └── workspace-Cg3kGb1y.js   ← 代码在这里
├── docs/
│   └── reference/
│       └── templates/
│           └── AGENTS.md        ← 模板在这里
└── package.json

FALLBACK_TEMPLATE_DIR 用了 ../../docs/reference/templates——从 dist/ 往上跳两级。但 dist/ 只在包根目录下一级,往上两级就跑到包外面了。

在正常的 main session 里,packageRoot 解析正确,第一个候选就能命中。但在 isolated cron session 里,resolveOpenClawPackageRoot 返回 null(因为执行环境不同),只能靠 cwd 和 fallback——而 Gateway daemon 的 cwd 是 home 目录,fallback 路径又是错的。

三个候选全军覆没,于是报错。

为什么时好时坏?

packageRoot 的解析依赖运行时环境。Main session 和 interactive session 的环境变量、模块解析上下文不同于 isolated cron session。前天可能是系统负载低、缓存命中了某些路径,或者 cron session 的初始化顺序恰好让 packageRoot 解析成功了。这种环境差异导致的 heisenbug 最难排查。

修复

修复很简单——加一个从 dist/ 往上一级的候选路径:

// 原始
const FALLBACK_TEMPLATE_DIR = path.resolve(
  path.dirname(fileURLToPath(import.meta.url)), 
  "../../docs/reference/templates"
);

// 新增:从 dist/ 往上一级(即包根目录)
const FALLBACK_TEMPLATE_DIR_BUNDLED = path.resolve(
  path.dirname(fileURLToPath(import.meta.url)), 
  "../docs/reference/templates"
);

同时调整候选列表的优先级:

const candidates = [
  // 1. 标准包根目录
  packageRoot ? path.join(packageRoot, "docs", "reference", "templates") : null,
  // 2. 打包后的相对路径(从 dist/ 往上一级)
  FALLBACK_TEMPLATE_DIR_BUNDLED,
  // 3. 原始 fallback(源码布局)
  FALLBACK_TEMPLATE_DIR,
  // 4. cwd 降级(优先级最低)
  cwd ? path.resolve(cwd, "docs", "reference", "templates") : null
].filter(Boolean);

重启 Gateway,手动触发 cron 任务——之前 8ms 秒失败的任务现在正常执行了。

经验总结

这类问题在 Node.js 项目打包后特别常见。根本原因是 __dirname / import.meta.url 的相对路径在源码和打包产物之间不一致。几个经验:

  1. 打包后的目录层级可能和源码不同../../src/agents/ 里能找到 docs/,在 dist/ 里就跑飞了
  2. Fallback 路径要覆盖多种布局。至少要考虑源码布局、打包布局、monorepo 布局
  3. Daemon 进程的 cwd 不可预测。不要依赖 process.cwd() 做路径兜底
  4. Isolated session ≠ main session。环境变量、模块解析上下文可能完全不同,必须在隔离环境下测试

相关链接

  • OpenClaw GitHub: https://github.com/openclaw/openclaw
  • 相关 PR: https://github.com/openclaw/openclaw/pull/43441
  • OfoxAI(我们用来跑 OpenClaw 的 API 提供商): https://ofox.ai

如果你也在用 OpenClaw 跑自动化任务碰到了类似问题,欢迎评论区交流。关注我,后续会分享更多 AI Agent 运维踩坑实录。

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐