直接运行Node.js文件正常但Cron执行失败,出现ERR_REQUIRE_ESM错误求助
直接运行Node.js文件正常但Cron执行失败,出现ERR_REQUIRE_ESM错误求助
这种情况我之前排查过好多次,核心问题基本都出在Cron的执行环境和你日常登录shell的环境不一致上,咱们一步步来拆解问题和尝试解决:
1. 先解决最常见的「环境/路径差异」问题
你在终端手动运行脚本时,用的是当前用户的完整环境变量(比如NODE_PATH、PATH里的Node版本路径),而且工作目录是项目根目录;但Cron的执行环境非常精简,默认工作目录是/,环境变量只有系统最基础的配置,这会导致两个关键问题:
- Node可能找不到正确的模块解析路径
- 甚至可能用的不是你日常使用的Node版本(虽然日志里显示是v20.11.0,但还是要确认)
快速尝试修复:在Cron命令里先切换工作目录
把你的Cron任务改成先进入项目目录再执行脚本,这一步往往能解决80%的路径相关问题:
00 01 * * * cd /home/adrian/nfp-repo/login && node upd_ATC_ALC_daily_from_server_file.js >> /tmp/crlog.log 2>&1 | logger -t mycmd
进阶确认:打印Cron的环境信息
如果切换目录后还是不行,可以先在Cron里打印环境变量和Node版本,对比和你终端的差异:
00 01 * * * echo "=== Cron Environment ===" >> /tmp/crlog.log && node -v >> /tmp/crlog.log && echo $PATH >> /tmp/crlog.log && cd /home/adrian/nfp-repo/login && node upd_ATC_ALC_daily_from_server_file.js >> /tmp/crlog.log 2>&1 | logger -t mycmd
然后对比终端里运行node -v和echo $PATH的结果,要是发现Node版本不一样,就用终端里which node得到的完整路径替换Cron里的node,比如:
00 01 * * * cd /home/adrian/nfp-repo/login && /home/adrian/.nvm/versions/node/v20.11.0/bin/node upd_ATC_ALC_daily_from_server_file.js >> /tmp/crlog.log 2>&1 | logger -t mycmd
2. 针对日志里的「ES模块兼容」报错
日志明确说@exodus/bytes是ES模块,但html-encoding-sniffer用CommonJS的require()去加载它,Node.js v14+就不允许这种跨模块加载了。那为什么手动运行没问题?
- 大概率是你手动运行时,项目根目录的
package.json里有"type": "module"配置,让Node自动把整个项目当作ES模块处理,兼容了这种加载方式;但Cron默认在/目录执行,找不到项目的package.json,所以用了默认的CommonJS规则,就触发了报错。 - 解决这个的核心还是让Cron在项目目录下执行脚本,这样Node能读到
package.json的配置,和你手动运行时的规则一致。
3. 极端情况的临时修复
如果以上方法都不行,也可以尝试修改依赖的加载方式(不推荐,属于临时hack,下次npm install会被覆盖):
找到/home/adrian/nfp-repo/login/node_modules/html-encoding-sniffer/lib/html-encoding-sniffer.js里的报错行,把:
const { getBOMEncoding, labelToName } = require("@exodus/bytes/encoding-lite.js");
改成动态import()的方式:
let getBOMEncoding, labelToName; (async () => { const module = await import("@exodus/bytes/encoding-lite.js"); getBOMEncoding = module.getBOMEncoding; labelToName = module.labelToName; // 后续依赖这两个方法的逻辑也要放在async函数里,或者用.then()处理 })();
备注:内容来源于stack exchange,提问作者Adrian
相关产品推荐
相关产品推荐

