Regression in 2.35.3: several mutating Array methods on $json return null

这是 n8n 2.35.x 升级后,新默认的 VM 表达式引擎与只读数据代理组合导致的回归。优先尝试将表达式改为数组拷贝后再调用方法(如 {{ [...$json.data].sort() }} ),或通过环境变量 N8N_EXPRESSION_ENGINE=legacy 临时切回旧引擎。

快速结论:这是 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() }}splicefillcopyWithin 的拷贝先行变体也已通过测试。

注意事项:设置 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),以阻止表达式写入工作流数据。原生的原地修改方法(sortsplicefillcopyWithin)必须通过 [[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 版本关系不大,主要受表达式引擎影响。
  • 排查工作流中是否有其他表达式依赖“原地修改后继续使用原数组”的行为,这会影响切换引擎后的结果一致性。

解决步骤

  1. 优先方案(已验证):将所有对 $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() }}
  2. 可优先尝试:在 Docker 容器环境中设置 N8N_EXPRESSION_ENGINE=legacy 并重启 n8n,以恢复旧引擎行为。注意:此方案原作者未独立复测,且可能改变其他表达式的执行语义。
  3. 如果依赖原地修改语义:考虑将 n8n 固定回 2.34.5,直到官方发布修复版本。Issue 评论确认 2.34.5 上所有列出的表达式均正常工作。
  4. 长期修复(上游):社区已定位根因并提出修复方案——为 sortsplicefillcopyWithin 注册拷贝优先的 shim,使 VM 引擎与旧引擎返回一致的非修改语义。可关注 n8n 后续版本是否合入此修复。

验证方法

Edit Fields (Set) 节点中,使用 Issue 提供的输入数据(10 个水果字符串数组),分别创建以下字段并检查输出:

  • {{ $json.data.sort() }} → 修复后应返回排序后的数组,而不是 null
  • {{ $json.data.splice(0, 2) }} → 修复后应返回被移除的元素数组。
  • {{ [...$json.data].sort() }} → 应始终返回排序后的数组。

同时对比同一工作流在 N8N_EXPRESSION_ENGINE=legacy 下的执行结果,确认两种引擎下输出一致(非修改语义)。

参考来源

n8n-io/n8n #36540

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 20699

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注