问题复现:那些让你抓狂的红色报错
作为 N8N大学 的首席主编,笔者见过太多新手在 Function 节点面前败下阵来。你满心欢喜地拖拽出一个 Function 节点,准备用 JavaScript 大展拳脚,结果一运行,满屏的红色错误提示瞬间让你头大。
最常见的报错大概长这样:Cannot read property 'xxx' of undefined 或者 ReferenceError: items is not defined。这些错误看似五花八门,但归根结底,往往是因为你没搞懂 n8n Function 节点的运行机制。
笔者注:n8n 的 Function 节点不是普通的浏览器环境,也不是 Node.js 的原生环境,它是一个高度封装的沙盒。用错语法,报错是必然的。
原因分析:为什么你的代码总是“水土不服”?
用大白话说,n8n 的 Function 节点是基于 vm2 或类似的沙盒技术运行的。这意味着它有自己的作用域和规则,不能完全照搬你在 VS Code 里写的 Node.js 代码。
报错的主要原因通常有以下三点:
- 作用域污染:你试图访问全局变量,但在 n8n 的沙盒里,很多全局对象是不可见的。
- 异步处理不当:JavaScript 是单线程的,如果你在 Function 节点里写了复杂的异步逻辑却没正确处理 Promise,数据流就会断掉。
- 数据结构理解错误:n8n 的数据在节点间传递是 JSON 格式的,如果你直接操作
items[0].json而不检查它是否存在,一旦上游节点没数据,直接就崩了。
解决方案:从入门到精通的三步走
别急,下面笔者带你一步步解决这些报错。我们从最简单的配置开始,直到写出健壮的代码。
第一招:学会“抄作业”——正确使用示例代码
n8n 官方其实提供了很多示例,但很多新手直接复制粘贴就报错。为什么?因为示例代码通常是针对特定数据结构的。
避坑指南: 当你复制一段代码时,先看懂它在操作什么。
- 如果代码里是
item.json.name,请确保你的上游节点输出的 JSON 里真的有name字段。 - 如果你不知道上游输出什么,先加一个 Debug 节点 或者 Set 节点,看看数据到底长什么样。
第二招:掌握 n8n 的数据模型——$item() 和 .json
这是 n8n Function 节点最核心的概念。n8n 的数据流是以“Item”(条目)为单位的。你的代码必须显式地处理这些 Item。
一个标准的、不会报错的结构通常是这样的:
// 遍历每一个传入的 item
for (const item of $input.all()) {
// 读取数据
let rawData = item.json;
// 处理逻辑(例如:修改字段)
rawData.new_field = "我是新值";
// 必须返回 item,否则数据会丢失
return item;
}
或者更简洁的写法(推荐):
return $input.all().map(item => {
item.json.processed = true;
return item;
});
重点: 如果你不使用 $input.all() 或者 $item(),而是直接写 items[0],在某些 n8n 版本或特定配置下,你很可能遇到 items is not defined 的错误。
第三招:处理异步与外部请求
如果你需要在 Function 节点里调用外部 API,你必须使用 async/await。但是,n8n 原生不支持在 Function 节点里直接使用 axios 或 fetch。
如果你强行引入 require('axios'),大概率会报错 Cannot find module。
正确的做法:
- 如果需要外部请求,尽量使用 HTTP Request 节点,因为它自带重试、分页和错误处理机制。
- 如果非要在 Function 节点里做复杂逻辑,确保你的代码是同步的,或者使用 n8n 支持的沙盒 API(虽然有限)。
- 处理错误:使用
try...catch包裹你的逻辑,防止整个 Workflow 因为一个数据报错而停滞。
try {
// 你的逻辑
let value = item.json.some_deep_property.nested.value;
// ...
} catch (error) {
// 捕获错误并记录,而不是让程序崩溃
console.error("处理 item 失败:", error.message);
// 可以选择跳过这个 item,或者标记它
return item;
}
预防措施:如何写出“不报错”的健壮代码
在 N8N大学 的实战经验里,预防报错的核心在于 防御性编程。
1. 空值检查(Null Check):
在访问深层嵌套属性前,永远先确认父级是否存在。
// 错误写法 let city = item.json.user.address.city; // 如果 address 为 null,直接报错 // 正确写法 let city = item.json.user && item.json.user.address ? item.json.user.address.city : 'Unknown'; // 或者使用可选链(如果 n8n 环境支持) let city = item.json.user?.address?.city || 'Unknown';
2. 数据类型转换:
n8n 传过来的字符串数字,不会自动转为数字。计算前先转换。
let price = Number(item.json.price); if (isNaN(price)) price = 0;
3. 善用 Code 节点而非 Function 节点:
n8n 新版(0.x 及 1.x)中,Code 节点(使用 JavaScript)逐渐取代了旧版的 Function 节点。如果你还在用旧版,请尝试切换到 Code 节点,它的 API 更现代,更接近 Node.js 原生体验。
FAQ 问答:你可能还想问
Q1: 为什么我写的 console.log 在日志里看不到?
在 n8n 的 Function 节点中,console.log 通常只会打印到 n8n 的后台控制台(如果你是 Docker 部署,就是容器的 logs),而不会显示在 UI 界面的 Debug 节点里。想在 UI 看结果,请使用 return item; 把数据传出去,或者使用 Debug 节点 显示数据。
Q2: 我想在 Function 节点里读取数据库,怎么办?
不推荐。Function 节点不应该承担重型计算或数据库连接的任务。这会阻塞 n8n 的主线程。正确的做法是使用 Postgres/MySQL 节点 或者 SSH 节点 来执行查询,将结果交给 Function 节点处理。
Q3: 代码在测试时没问题,正式运行就报错?
这通常是因为数据量级不同。测试时可能只有 1 条数据,正式运行有 100 条。如果你的代码里有全局变量(例如在循环外定义计数器),可能会因为作用域问题导致数据串扰。确保你的逻辑是无状态的,或者正确管理状态。
总结与资源
n8n 的 Function 节点是把双刃剑,用好了能处理极其复杂的逻辑,用不好就是报错的无底洞。记住笔者的核心建议:理解数据结构、做好空值检查、善用 $input API。
如果你在 n8n 的使用过程中还有其他疑难杂症,欢迎访问 N8N大学 (n8ndx.com),这里有更多实战案例和避坑指南等着你。