小白 / Xiaobai
开发者 · 产品构建者
持续构建 AI 工程系统、开发者工具与长期数字资产。
关于作者与 XBSTACK →
n8n HTTP Request 返回 _readableState 而不是 JSON:Raw Body 为什么会变成 Stream?
n8n HTTP Request 使用 Raw Body 且显式选择 JSON Response 时,为什么输出会变成 _readableState/_writableState Stream?本文用四组对照复现问题,核对 n8n 2.34.5 源码,并给出已验证的临时处理方式。
n8n HTTP Request 返回 _readableState 而不是 JSON:Raw Body 为什么会变成 Stream?
如果你的 n8n HTTP Request 节点本来是在调用一个正常返回 JSON 的 API,但把 Body Content Type 设为 Raw、同时把 Response Format 显式设为 JSON 后,节点输出突然变成 _readableState、_writableState、bytesWritten、_events 这一类字段,先不要急着改目标 API。截至 2026 年 8 月 18 日,我在本机 n8n 1.112.4 上用同一个 POST 接口做了四组对照,只有 Raw Body + 显式 JSON 这一组返回了 Stream 内部对象;把 Body 改成 JSON,或者保持 Raw 但让 Response Format 使用 Auto-detect,响应都能正常被解析。
我随后核对了 [email protected] 标签对应的 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,节点输出应该是普通对象,例如 data、headers、json、url 等业务字段。如果输出反而变成下面这种 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 标记。

| 组别 | Body Content Type | Response Format | 本机 n8n 1.112.4 结果 |
|---|---|---|---|
| A | Raw | JSON | 异常:返回 Stream 内部字段 |
| B | JSON | JSON | 正常解析 JSON |
| C | Raw | Auto-detect | 正常解析 JSON |
| D | Raw | Text | 正常返回 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,马上恢复正常,返回对象里能看到 args、data、headers、json、url,并且 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
继续看 [email protected] 标签源码,HTTP Request V3 在准备 request options 时有两条关键分支。

第一条:只要 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 与超时 一起设计,而不是看到字段为空就直接重放请求。
因此排查顺序应该是:
- 先看 HTTP 状态和目标系统是否真的执行成功;
- 再看 n8n 输出结构是不是 Stream 内部对象;
- 用 JSON Body + JSON Response 做一组对照;
- 用 Raw Body + Auto-detect 再做一组对照;
- 最后才决定是修改请求格式、加临时兼容还是等待上游修复。
版本边界:我能证明什么,不能证明什么
这篇文章必须把版本口径说清楚。
我本机当前可直接运行的是 n8n 1.112.4,这个版本完成了四组真实 HTTP 请求,并稳定得到上面的对照结果。我尝试直接运行 [email protected],但该版本要求 Node.js >=22.22,本机当时是 22.18.0;Docker CLI 虽然存在,但 Docker daemon 没有启动。因此我没有为了赶文章去绕过运行时要求,也不会写成“XBSTACK 已在 2.34.5 端到端复现”。
对 2.34.5,我完成的是另一层验证:直接核对 [email protected] 标签源码,确认 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,我会按下面的优先级处理:

图中第三种“手动消费 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 参数依赖回归。
继续按 n8n 生产排障链路读
自托管、Queue Mode、Webhook、错误处理和案例文统一沉淀到 Workflow 专题页:部署文做主力页,案例文做长尾页,对比文承接工具选择流量。
继续阅读
返回专题 →AI 工程周报
只发真正改变工程判断的变化、故障、实验和新资产。
参与讨论
问题、验证与勘误
登录后可发表评论。所有新评论先进入审核;审核期间仅评论者本人和管理员可见,通过后才公开。