快速结论:这是 n8n 2.35.x 升级后,新默认的 VM 表达式引擎与只读数据代理组合导致的回归。优先尝试将表达式改为数组拷贝后再调用方法(如 {{ [...$json.data].sort() }}),或通过环境变量 N8N_EXPRESSION_ENGINE=legacy 临时切回旧引擎。
适用环境:n8n 2.35.3(Docker 自托管)、Node.js v24.18.1、SQLite 数据库、Ubuntu 24 (Docker Host)、Chrome 浏览器客户端。
最快修复方案:在 Edit Fields (Set) 节点的表达式中,先复制数组再调用会修改数组的方法。已验证有效的写法:{{ [...$json.data].sort() }}、{{ $json.data.slice().sort() }};splice、fill、copyWithin 的拷贝先行变体也已通过测试。
注意事项:设置 N8N_EXPRESSION_ENGINE=legacy 可以恢复旧行为,但原作者未独立复测该方案,属于“可优先尝试”。此外,旧引擎下这些方法会直接修改底层数组,与 VM 引擎的非修改语义不同,切换引擎前需要检查工作流是否依赖“原地修改”行为。
问题场景
用户将 n8n 从 2.34.5 升级到 2.35.3 后,在 Edit Fields (Set) 节点中使用表达式对 $json 内的数组直接调用 sort()、splice()、fill()、copyWithin() 等会修改原数组的方法时,表达式结果变成 null。相同表达式在 2.34.5 上返回正常结果。
报错原文
Regression in 2.35.3: several mutating Array methods on $json return null
{{ $json.data.sort() }}
{{ $json.data.splice(0, 2) }}
{{ $json.data.fill("X", 0, 2) }}
{{ $json.data.copyWithin(0, 2, 4) }}
Each of these returns: null
原因分析
根因分析指向两处代码路径的组合问题:
- VM 引擎的只读数据代理:默认的 VM 表达式引擎通过
createDeepLazyProxy将工作流数据包装为深层懒代理,数组索引属性被标记为只读(writable: false),以阻止表达式写入工作流数据。原生的原地修改方法(sort、splice、fill、copyWithin)必须通过[[Set]]写入索引,但写入失败并被表达式错误路径吞掉,最终观测结果为null。 - 引擎间语义不一致:旧引擎没有为这些方法添加拷贝优先的 shim,因此它们会原生执行并修改原数组;而
reverse()因为已有拷贝优先的扩展 shim,所以不受影响。这解释了为何[...$json.data].sort()正常——它先物化拷贝,绕开了代理。
环境排查
- 确认 n8n 版本是否为 2.35.3 或更高(2.35.x 系列);2.34.5 及更早版本不受影响。
- 检查 Docker 环境变量是否已设置
N8N_EXPRESSION_ENGINE;如果设置为legacy,问题可能不会出现。 - Node.js 版本:Issue 报告为 24.18.1,但该问题与 Node 版本关系不大,主要受表达式引擎影响。
- 排查工作流中是否有其他表达式依赖“原地修改后继续使用原数组”的行为,这会影响切换引擎后的结果一致性。
解决步骤
- 优先方案(已验证):将所有对
$json数组直接调用修改方法的表达式,改为先复制再调用:
{{ [...$json.data].sort() }}
{{ [...$json.data].splice(0, 2) }}
{{ [...$json.data].fill("X", 0, 2) }}
{{ [...$json.data].copyWithin(0, 2, 4) }}
或使用非修改方法:{{ $json.data.slice().sort() }}。 - 可优先尝试:在 Docker 容器环境中设置
N8N_EXPRESSION_ENGINE=legacy并重启 n8n,以恢复旧引擎行为。注意:此方案原作者未独立复测,且可能改变其他表达式的执行语义。 - 如果依赖原地修改语义:考虑将 n8n 固定回 2.34.5,直到官方发布修复版本。Issue 评论确认 2.34.5 上所有列出的表达式均正常工作。
- 长期修复(上游):社区已定位根因并提出修复方案——为
sort、splice、fill、copyWithin注册拷贝优先的 shim,使 VM 引擎与旧引擎返回一致的非修改语义。可关注 n8n 后续版本是否合入此修复。
验证方法
在 Edit Fields (Set) 节点中,使用 Issue 提供的输入数据(10 个水果字符串数组),分别创建以下字段并检查输出:
{{ $json.data.sort() }}→ 修复后应返回排序后的数组,而不是null。{{ $json.data.splice(0, 2) }}→ 修复后应返回被移除的元素数组。{{ [...$json.data].sort() }}→ 应始终返回排序后的数组。
同时对比同一工作流在 N8N_EXPRESSION_ENGINE=legacy 下的执行结果,确认两种引擎下输出一致(非修改语义)。
参考来源
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。


