小白 / Xiaobai
开发者 · 产品构建者
持续构建 AI 工程系统、开发者工具与长期数字资产。
关于作者与 XBSTACK →
n8n 2.35.3 里 $json.data.sort() 为什么返回 null?Array 方法回归与修复
n8n 2.35.3 的 Edit Fields 中,$json.data.sort()、splice()、fill()、copyWithin() 为什么突然返回 null?本文用官方 Docker 镜像对照 2.34.5 与 2.35.3,确认版本回归,并验证复制数组后的临时修复。
如果你刚把 n8n 升到 2.35.3,然后发现 Edit Fields 里原本正常的:
{{ $json.data.sort() }}
突然变成:
null
这次可以先别去改输入 JSON,也不用怀疑 JavaScript 的 sort() 语义。我在本机用官方 n8nio/n8n:2.34.5 与 n8nio/n8n:2.35.3 镜像做了隔离对照,已经确认这是一个从 2.34.5 到 2.35.3 出现的可复现回归。
更关键的是,它不只影响 sort()。我实际跑过的四个直接变异调用:
{{ $json.data.sort() }}
{{ $json.data.splice(0, 2) }}
{{ $json.data.fill("X", 0, 2) }}
{{ $json.data.copyWithin(0, 2, 4) }}
在 2.35.3 的 Edit Fields 中都会得到 null。但 reverse() 正常,先复制数组再调用这些方法也正常。
如果你现在只想先恢复工作流,最短答案是:
不要直接改 $json.data
先复制,再操作副本
例如:
{{ [...$json.data].sort() }}
我不只验证了 sort(),splice()、fill()、copyWithin() 的 copy-first 写法也都在 2.35.3 通过。

我为什么把它判断为版本回归,而不是表达式写错
上游 n8n Issue #36540 在 2026 年 8 月 18 日报告了同一现象:从 2.34.5 升级到 2.35.3 后,Edit Fields 中多个直接作用于 $json 数组的变异方法开始返回 null。报告中列出的 sort()、splice()、fill()、copyWithin() 与我本机的结果一致;报告者同时指出 reverse() 和复制数组后的 sort() 正常。
但仅仅重复 Issue 里的结论,不足以支撑一篇排障文章。所以我没有直接写,而是把 2.34.5 和 2.35.3 两个官方 Docker 镜像都拉下来,用同一份输入、同一种 Edit Fields 节点、同一个表达式结构做版本对照。
实验基础环境:
| 项目 | 对照 A | 对照 B |
|---|---|---|
| n8n | 2.34.5 | 2.35.3 |
| Docker image | n8nio/n8n:2.34.5 | n8nio/n8n:2.35.3 |
| Node.js | v24.18.0 | v24.18.1 |
| 数据库 | SQLite 默认配置 | SQLite 默认配置 |
| 执行方式 | n8n execute | n8n execute |
| Edit Fields | 同一节点结构 | 同一节点结构 |
每一个方法都使用一条独立输入分支,输入固定为:
{
"data": [
"Mango",
"Apple",
"Kiwi",
"Orange",
"Blueberry",
"Banana",
"Peach",
"Grape",
"Pineapple",
"Strawberry"
]
}
之所以要“每个方法独立一份输入”,是因为我第一轮把多个表达式都塞在一个 Edit Fields 节点时,2.34.5 会真的执行原地变异:前一个表达式可能已经改掉 $json.data,后一个表达式看到的就不是原始数组。那个现象本身也值得注意,但不适合作为严谨的版本矩阵。所以最终结论只采用隔离后的结果。
2.34.5 vs 2.35.3:实际结果
最终矩阵如下:

| 表达式 | n8n 2.34.5 | n8n 2.35.3 |
|---|---|---|
$json.data.sort() | 返回排序后的数组 | null |
$json.data.splice(0, 2) | ['Mango','Apple'] | null |
$json.data.fill('X', 0, 2) | 返回前两项被替换后的数组 | null |
$json.data.copyWithin(0, 2, 4) | 返回原地复制后的数组 | null |
$json.data.reverse() | 正常 | 正常 |
[...$json.data].sort() | 正常 | 正常 |
$json.data.slice().sort() | 正常 | 正常 |
这里最重要的不是“某个函数偶发失败”,而是三件事同时成立:
- 输入和节点配置不变,只切换 n8n 版本,结果改变。
- 不是所有 Array 方法都坏。
reverse()在 2.35.3 正常。 - **不是排序能力坏了。**复制数组后的
sort()和slice().sort()都正常。
这让问题范围明显收窄:它表现为 2.35.3 对直接挂在 $json 输入数组上的部分变异方法进行了不同处理。至于内部究竟是哪一层代理、只读保护、表达式沙箱或序列化逻辑导致,当前上游 Issue 还没有给出可以引用的修复提交,因此我不会在这里把“表现”强行写成“根因”。
最快的临时修复:先复制数组
对于现在已经在生产上报错或产生 null 的工作流,我更建议先做最小变更,不要为了绕过一个表达式回归就把整条工作流改成 Code 节点。

sort()
原来:
{{ $json.data.sort() }}
改成:
{{ [...$json.data].sort() }}
或者:
{{ $json.data.slice().sort() }}
两种我都在 2.35.3 验证通过。
splice()
原来:
{{ $json.data.splice(0, 2) }}
改成:
{{ [...$json.data].splice(0, 2) }}
本机 2.35.3 返回:
["Mango", "Apple"]
要注意 splice() 的 JavaScript 语义本来就是“返回被删除的元素”,不是返回删除后的剩余数组。不要因为修复了 null,又把业务语义改错。
fill()
原来:
{{ $json.data.fill("X", 0, 2) }}
改成:
{{ [...$json.data].fill("X", 0, 2) }}
2.35.3 实测返回:
["X", "X", "Kiwi", "Orange", "Blueberry", "Banana", "Peach", "Grape", "Pineapple", "Strawberry"]
copyWithin()
原来:
{{ $json.data.copyWithin(0, 2, 4) }}
改成:
{{ [...$json.data].copyWithin(0, 2, 4) }}
2.35.3 实测返回:
["Kiwi", "Orange", "Kiwi", "Orange", "Blueberry", "Banana", "Peach", "Grape", "Pineapple", "Strawberry"]
这四组不是“理论上应该能用”,而是同一台机器、同一个 2.35.3 官方镜像里实际执行通过的结果。
为什么复制数组反而是更稳的写法
即使上游之后修复了这个回归,我仍倾向于在 n8n 表达式里少直接修改 $json 提供的输入对象。
n8n 的数据映射本质上是在当前节点里引用前序节点产生的数据。对于 sort()、splice()、fill()、copyWithin() 这类原地修改数组的方法,如果直接对 $json.data 调用,你同时做了两件事:读取输入、修改输入对象。尤其当一个节点里有多个字段表达式、或者后面还有表达式依赖同一个对象时,副作用会让排错变得困难。
复制后再变异:
{{ [...$json.data].sort() }}
至少把“我要得到一个排序结果”和“我要修改当前输入数组”分开了。对于对象数组,需要根据数据结构决定浅拷贝是否足够,但对本次字符串数组场景,这个边界非常清楚。
是否应该直接降级到 2.34.5
我不会把“降级”写成第一推荐,因为是否回滚取决于工作流数量和生产风险。
如果受影响的地方只有几个 Edit Fields 表达式,copy-first 改法通常比整体回退版本影响更小。你可以先全局搜索:
.sort(
.splice(
.fill(
.copyWithin(
然后重点检查这些调用是不是直接挂在 $json.xxx 数组上。
如果你的实例里有大量工作流依赖这种写法,又没有时间逐条回归,版本回退才可能更合适。但回退 n8n 不能只看一个节点表达式,还要考虑当前数据库迁移、其他节点升级、凭据和生产部署策略。至少先备份数据库和工作流,再在测试实例验证 2.34.5。
升级到 2.35.3 后,我建议这样排查
如果你的症状和本文相似,可以按这个顺序做:
- 确认实例实际版本确实是 2.35.3,而不是浏览器缓存或 worker/main 版本不一致。
- 找到返回
null的 Edit Fields 字段,保留原表达式。 - 用同一输入改成
[...$json.xxx]的 copy-first 版本做 A/B。 - 如果 copy-first 正常,继续检查是否属于
sort/splice/fill/copyWithin这一类。 - 不要一次性重构整条工作流,先恢复最小故障点。
- 对生产环境做一次全局扫描,找出其他直接修改
$json数组的表达式。 - 上游修复发布后,再在测试环境撤掉 workaround 做回归,不要直接在生产批量还原。
这和昨天的 n8n HTTP Request Stream 问题不是一回事
XBSTACK 昨天刚写过一篇 n8n HTTP Request Raw Body 返回 _readableState / Stream 的排障。两篇看起来都属于 n8n,但搜索意图完全不同。
昨天的问题发生在 HTTP Request 节点的 Raw Body 与 Response Format 组合,症状是本应得到 JSON,却暴露 Stream 内部对象。
今天这个问题发生在 Edit Fields 表达式求值,症状是升级 2.35.3 后,对 $json 数组直接调用部分变异方法得到 null。
因此这不是为了追 n8n 热点重复发内容,而是两个可以独立复现、独立搜索、独立修复的故障类。更通用的 n8n 失败处理可以继续看 n8n Workflow 错误处理 和 Workflow 专题。
当前证据边界
截至 2026 年 8 月 19 日,我能明确确认的是:
- 上游 n8n Issue #36540 于 8 月 18 日报告了 2.35.3 的同类回归。
- XBSTACK 使用官方 2.34.5 与 2.35.3 Docker 镜像完成了本地版本对照。
sort/splice/fill/copyWithin的 direct-on-$json调用在 2.35.3 稳定返回null。reverse()没有表现出同样问题。- 对数组先复制后再执行四个受影响方法,在 2.35.3 全部通过。
我暂时不会写“根因已经确定”“某个 commit 导致”或“2.35.4 一定修复”,因为目前没有上游修复提交可以支撑这些结论。
上游跟踪:
- n8n Issue #36540: Regression in 2.35.3: several mutating Array methods on $json return null
- n8n 官方文档:Referencing data in the UI
如果上游后续合并修复,我会把这篇文章更新成 受影响版本 → 修复版本 → workaround 是否仍需要 的版本矩阵,而不是再新建一个重复 URL。
继续按 n8n 生产排障链路读
自托管、Queue Mode、Webhook、错误处理和案例文统一沉淀到 Workflow 专题页:部署文做主力页,案例文做长尾页,对比文承接工具选择流量。
继续阅读
返回专题 →AI 工程周报
只发真正改变工程判断的变化、故障、实验和新资产。
参与讨论
问题、验证与勘误
登录后可发表评论。所有新评论先进入审核;审核期间仅评论者本人和管理员可见,通过后才公开。