XBSTACK XBSTACK
小白 / Xiaobai

小白 / Xiaobai

开发者 · 产品构建者

持续构建 AI 工程系统、开发者工具与长期数字资产。

关于作者与 XBSTACK →
n8n HTTP Request Raw Body 与 JSON Response 组合导致返回 Stream 对象的排障示意

n8n HTTP Request 返回 _readableState 而不是 JSON:Raw Body 为什么会变成 Stream?

n8n HTTP Request 使用 Raw Body 且显式选择 JSON Response 时,为什么输出会变成 _readableState/_writableState Stream?本文用四组对照复现问题,核对 n8n 2.34.5 源码,并给出已验证的临时处理方式。

发布 · 2026-08-1810 分钟阅读XBSTACK 原创
#n8n#HTTP Request#Raw Body#JSON#Stream#Debugging#Workflow

n8n HTTP Request 返回 _readableState 而不是 JSON:Raw Body 为什么会变成 Stream?

如果你的 n8n HTTP Request 节点本来是在调用一个正常返回 JSON 的 API,但把 Body Content Type 设为 Raw、同时把 Response Format 显式设为 JSON 后,节点输出突然变成 _readableState_writableStatebytesWritten_events 这一类字段,先不要急着改目标 API。截至 2026 年 8 月 18 日,我在本机 n8n 1.112.4 上用同一个 POST 接口做了四组对照,只有 Raw Body + 显式 JSON 这一组返回了 Stream 内部对象;把 Body 改成 JSON,或者保持 Raw 但让 Response Format 使用 Auto-detect,响应都能正常被解析。

我随后核对了 n8n@2.34.5 标签对应的 HttpRequestV3.node.ts。源码里 Raw Body 分支仍会把 requestOptions.useStream 设为 true,而把 Stream 读取成字符串、再根据 Content-Type 转成 JSON/Text 的代码位于 autoDetectResponseFormat 分支内。这和 2026 年 8 月 17 日提交的 n8n Issue #36402 描述的是同一条故障链。需要说明的是:我的完整运行级复现版本是 1.112.4;2.34.5 是源码确认,不是我本机的完整端到端执行结果。

最快的处理方式也很明确:如果你发送的内容本质上就是 JSON,先把 Body Content Type 改为 JSON;如果业务必须发送 Raw Body,则先把 Response Format 改回 Auto-detect,然后用真实目标接口复测。这个问题属于 n8n / Workflow 生产排障 的响应处理层,不是普通的 API 业务错误;下面把为什么会发生、四组实验怎么做、哪些处理已经验证、哪些还不能下结论完整拆开。

先确认你遇到的是不是同一个问题

这个问题的症状很有辨识度。正常情况下,HTTP Request 调一个 JSON API,节点输出应该是普通对象,例如 dataheadersjsonurl 等业务字段。如果输出反而变成下面这种 Node.js 对象内部状态,就值得优先检查本文这条路径:

_readableState
_writableState
_writeState
_events
bytesWritten
_handle
_outBuffer

关键不是“接口里有没有 Stream”,而是 n8n 有没有把收到的 Stream 消费并转换成你要求的 JSON。我这次故意让四个节点请求完全相同的 https://postman-echo.com/post,只改变 Body Content Type 和 Response Format,这样就能把目标 API、网络和返回结构尽量从变量里拿掉。

公开 Issue #36402 的报告者使用 n8n 2.34.5、Node.js 24.13.1、PostgreSQL、自托管环境,描述的现象也是 Raw Body + JSON Response 后输出 _readableState / _writableState。Issue 已经被打上 Nodes 团队相关标签。对排障来说,这比单纯搜索到一段“把 Response Format 改一下”的经验更有价值,因为它给出了明确的版本、配置组合和源码方向。

四组对照:只有 Raw Body + 显式 JSON 返回了 Stream 内部对象

我导入了一个只包含 Manual Trigger 和四个 HTTP Request 分支的工作流。四个请求都 POST 到同一个 echo 接口,发送的内容也只是一个很小的 JSON 标记。

n8n HTTP Request 四组对照测试:Raw Body 加 JSON Response 返回流对象,其他组合返回解析后的 JSON 或文本

组别Body Content TypeResponse Format本机 n8n 1.112.4 结果
ARawJSON异常:返回 Stream 内部字段
BJSONJSON正常解析 JSON
CRawAuto-detect正常解析 JSON
DRawText正常返回 text wrapper

A 组的关键结果不是“HTTP 请求失败”。节点状态仍然是 success,但 json 里出现的是:

_readableState
_writableState
bytesWritten
_handle
_outBuffer
...

这恰恰是这个 Bug 比 400/500 更麻烦的地方:工作流可能认为节点成功了,但下游表达式已经拿不到原来的业务 JSON。 如果下游写的是 $json.id$json.data.xxx 或者基于返回字段判断支付、投递、状态更新,就可能把“请求成功但响应没有被解析”误判成业务失败。

B 组只把 Body Content Type 从 Raw 改成 JSON,Response Format 仍然保持 JSON,马上恢复正常,返回对象里能看到 argsdataheadersjsonurl,并且 echo 出来的 json.case 正确等于 json-json

C 组反过来保留 Raw Body,但不再显式指定 JSON,而是让 n8n Auto-detect,结果也恢复正常。这个对照非常重要,因为它说明问题不只是“Raw Body 不能发送 JSON”,而更像是 Raw Body 触发了 Stream 响应,同时显式 JSON 路径没有把这个 Stream 按 Auto-detect 路径消费掉。

完整 Workflow 和压缩后的结果日志会随本文公开:

根因为什么会落在 useStream 和 autoDetectResponseFormat

继续看 n8n@2.34.5 标签源码,HTTP Request V3 在准备 request options 时有两条关键分支。

n8n Raw Body 响应处理的错误路径与正确路径:Auto-detect 会消费 Stream 并解析 JSON,显式 JSON Response 可能直接暴露流对象

第一条:只要 response 是 Auto-detect 或 File,就会使用 Stream;除此之外,如果 request body 是 Raw,也会单独开启 Stream。逻辑可以简化成:

if (autoDetectResponseFormat || responseFormat === 'file') {
  requestOptions.useStream = true;
} else if (bodyContentType === 'raw') {
  requestOptions.json = false;
  requestOptions.useStream = true;
} else {
  requestOptions.json = true;
}

第二条在响应回来之后。源码会在 autoDetectResponseFormat 条件下读取 Stream,通过 Content-Type 判断它应该变成 JSON、Text 还是 File,并在 JSON/Text 情况下把流转换成字符串。问题在于:你显式选择 JSON 时,autoDetectResponseFormat 是 false;但前面的 Raw Body 分支又已经让 response 走 Stream。

于是会形成一个很反直觉的组合:

Raw request body
  ↓
useStream = true
  ↓
response 是 Stream
  ↓
显式 Response Format = JSON
  ↓
autoDetectResponseFormat = false
  ↓
不进入 Auto-detect 的 Stream 消费/转换代码
  ↓
Stream 对象被带进节点输出

这也解释了为什么“显式选择 JSON”没有像直觉那样更稳定,反而在这个组合下出问题;以及为什么保留 Raw、改回 Auto-detect 能在我的对照中恢复——Auto-detect 恰好进入了读取 Stream 的那条路径。

两个临时方案,我实际验证了哪一个

方案一:请求体本来就是 JSON,直接使用 JSON Body

这是我更推荐的处理。把:

Body Content Type: Raw
Response Format: JSON

改成:

Body Content Type: JSON
Response Format: JSON

在本次同接口对照中,响应正常恢复为解析后的 JSON。这个方案的优点是语义也更准确:如果你就是在发送标准 JSON,没有必要为了手写字符串而强制走 Raw。

但改完不能只看节点绿色。至少检查三件事:目标服务收到的 Content-Type 是否符合要求;body 是否因为 n8n JSON 序列化发生字符、转义或类型变化;下游依赖字段是否全部恢复。

方案二:必须保留 Raw Body 时,先用 Auto-detect

如果你在发签名后的原始字符串、特殊 JSON 方言、非 JSON 文本,或者服务端要求字节级内容不能被重新序列化,那么不能随便把 Raw 改成 JSON。这时可以先保留 Raw,把 Response Format 改为 Auto-detect。

本次 echo 对照中,这个组合能正常得到 JSON。但是它有一个边界:Auto-detect 依赖服务器返回的 Content-Type。如果你的上游明明返回 JSON 却写成 text/plain,或者错误响应和成功响应 Content-Type 不一致,最终解析形态可能和预期不同。因此它是已验证的临时绕过路径,不是“任何 API 都适用”的正式修复。

不建议用 Code 节点去硬拆 _outBuffer

看到 _outBuffer_readableState.buffer 里有原始字节后,一个很自然的想法是:既然数据其实还在,那我加个 Code 节点把 Buffer 转成字符串,再 JSON.parse() 不就行了?

这可以作为诊断手段,但不适合做长期生产方案。原因是你依赖的是 Node.js Stream 对象的内部序列化形态,而不是 n8n 对外承诺的 HTTP Request 输出契约;不同 Node.js、axios、压缩算法、响应大小、n8n 序列化和执行存储方式都可能改变这些内部字段。Issue 报告里还提到,几百字节响应被整个 Stream 对象序列化后,执行数据可能膨胀到非常大的体积。

生产处理应该尽量让 HTTP Request 节点自己回到正常的 JSON/Text 输出,而不是在下游长期解析 _readableState

为什么这类问题容易被误判成“供应商 API 坏了”

最危险的信号是:HTTP Request 节点显示成功。没有 500,没有 JSON parse error,也没有明显的 n8n 异常提示;只是返回结构悄悄从业务对象变成了 Stream 对象。

如果你的下一步是:

$json.delivery_id

现在它会变成 undefined。很多工作流随后会进入“没有 delivery_id = 投递失败”的业务分支,但真实情况可能是供应商已经成功处理,只是 n8n 没把成功响应解析出来。对于支付、邮件投递、工单创建、订单同步这类带副作用的 API,这种误判尤其危险:你如果自动重试,可能制造重复操作。这里的重试策略应该和站内的 n8n 错误处理:Error Workflow、Retry 与超时 一起设计,而不是看到字段为空就直接重放请求。

因此排查顺序应该是:

  1. 先看 HTTP 状态和目标系统是否真的执行成功;
  2. 再看 n8n 输出结构是不是 Stream 内部对象;
  3. 用 JSON Body + JSON Response 做一组对照;
  4. 用 Raw Body + Auto-detect 再做一组对照;
  5. 最后才决定是修改请求格式、加临时兼容还是等待上游修复。

版本边界:我能证明什么,不能证明什么

这篇文章必须把版本口径说清楚。

我本机当前可直接运行的是 n8n 1.112.4,这个版本完成了四组真实 HTTP 请求,并稳定得到上面的对照结果。我尝试直接运行 n8n@2.34.5,但该版本要求 Node.js >=22.22,本机当时是 22.18.0;Docker CLI 虽然存在,但 Docker daemon 没有启动。因此我没有为了赶文章去绕过运行时要求,也不会写成“XBSTACK 已在 2.34.5 端到端复现”。

对 2.34.5,我完成的是另一层验证:直接核对 n8n@2.34.5 标签源码,确认 Raw Body 设置 useStream=true、而 JSON/Text 的 Stream 读取主要在 Auto-detect 条件内的代码路径仍然存在。同时,上游 Issue #36402 报告的实际运行版本就是 2.34.5。

所以当前证据应准确表述为:

n8n 1.112.4 运行级复现 + n8n 2.34.5 标签源码确认 + 上游 2.34.5 用户报告。

这已经足以指导排障和临时规避,但在 n8n 发布正式修复后,仍需要重新跑一遍同样的四组矩阵,再决定是否删除 workaround。

最终处理建议

如果你今天就遇到了 _readableState,我会按下面的优先级处理:

n8n 返回流对象后的三种处理方式与排查顺序:优先 Auto-detect、按需关闭 Raw Body,并仅在诊断场景手动消费 Stream

图中第三种“手动消费 Stream”只适合作为诊断或临时验证手段,不建议把 _readableState_outBuffer 等内部结构当成长期生产接口;生产优先级仍以恢复 HTTP Request 节点的正常 JSON/Text 输出为主。

**第一优先:**请求体本来就是 JSON,就改成 JSON Body + JSON Response,并核验服务器收到的 body。

**第二优先:**必须保留 Raw,就测试 Raw + Auto-detect,同时核验成功和错误响应的 Content-Type。

**第三优先:**不要在生产里长期解析 _readableState / _outBuffer,也不要因为下游字段为空就盲目重试有副作用的 API。

**第四优先:**记录当前 n8n 版本、Node.js 版本、HTTP Request typeVersion、Body Content Type、Response Format 和最小工作流,关注 n8n Issue #36402 的修复状态。

如果你需要自己复核,直接使用这次的 公开最小复现。它只有一个 Trigger 和四个 HTTP Request 分支,不需要 API Key,能够很快判断你遇到的是不是同一类问题。

继续排查 n8n 生产问题,可以看 n8n Webhook Production URL、Auth 与 404 排查n8n 错误处理:Error Workflow、Retry 与超时;如果问题出现在升级后工作流无法激活,则更接近 n8n Baserow 参数依赖回归

专题入口 / AI Workflow Hub

继续按 n8n 生产排障链路读

自托管、Queue Mode、Webhook、错误处理和案例文统一沉淀到 Workflow 专题页:部署文做主力页,案例文做长尾页,对比文承接工具选择流量。

继续阅读

返回专题 →
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.33.4 升级后 Baserow 工作流无法激活:Could not resolve parameter dependencies 怎么解决?n8n 2.33.4 升级后 Baserow 工作流报 Could not resolve parameter dependencies. Max iterations reached 怎么办?本文对比 2.32.7 与 2.33.4 的 Baserow 参数树,复现同一错误,并给出回滚、验证与官方修复前的安全处理方式。n8n AI Agent 不调用工具怎么办?tool_choice、模型兼容与 Memory 排查n8n AI Agent 不调用工具:n8n AI Agent 明明连接了 Tool 却直接回答怎么办?本文基于官方 Issue 与本地请求契约实验,排查首轮 tool_choice、OpenAI 兼容模型、Tool Schema、描述冲突和 Memory 丢失。n8n 错误处理怎么做?Error Workflow、Retry On Fail、超时与失败重跑n8n 生产工作流怎么处理限流、超时、节点失败和失败重跑?本文拆解 Error Workflow、Retry On Fail、执行历史、数据保留、幂等、防重复写入和 AI 调用成本记录。

AI 工程周报

只发真正改变工程判断的变化、故障、实验和新资产。

评论与补充证据

参与讨论

问题、验证与勘误

登录后可发表评论。所有新评论先进入审核;审核期间仅评论者本人和管理员可见,通过后才公开。

登录评论 审核后公开
正在加载评论区…