Xiaobai
Developer · Builder
Building AI engineering systems, developer tools and long-term digital assets at XBSTACK.
About Xiaobai & XBSTACK →
n8n HTTP Request Returns _readableState Instead of JSON: Raw Body Response Stream Fix
n8n HTTP Request can return _readableState instead of JSON with Raw Body + explicit JSON Response. See the four-case repro, source path and tested workarounds.
n8n HTTP Request Returns _readableState Instead of JSON: Raw Body Response Stream Fix
If an n8n HTTP Request node calls an API that normally returns JSON, but its output suddenly turns into _readableState, _writableState, bytesWritten, _events and other Node.js stream internals after you set Body Content Type = Raw and Response Format = JSON, do not start by rewriting the upstream API. On August 18, 2026, I ran four equivalent POST requests on local n8n 1.112.4. Only Raw Body + explicit JSON returned a stream-shaped object. Switching the request body to JSON, or keeping Raw while returning Response Format to Auto-detect, produced parsed responses.
I then inspected the [email protected] tagged HttpRequestV3.node.ts. The Raw Body path still sets requestOptions.useStream = true, while the code that consumes a stream, inspects Content-Type and converts it into JSON or text sits inside autoDetectResponseFormat. That is the same failure mechanism described in n8n issue #36402, filed on August 17. One evidence boundary matters: my complete runtime control is on 1.112.4; 2.34.5 is tagged-source confirmation, not a full end-to-end run on my machine.
The fastest containment is straightforward. If your outgoing payload is ordinary JSON, change Body Content Type to JSON. If you must preserve Raw Body semantics, test Response Format = Auto-detect and validate the real endpoint. This belongs to the n8n / Workflow production troubleshooting cluster, not to ordinary application-level API debugging. The rest of this article shows the four-case control, the source path behind the behavior, which workarounds were actually tested, and what remains unverified.
First check whether this is the same failure
A normal JSON HTTP Request output should contain application fields: data, headers, json, url, or whatever the upstream response defines. If you instead see fields such as these, investigate this path first:
_readableState
_writableState
_writeState
_events
bytesWritten
_handle
_outBuffer
The question is not whether the remote server ever uses streaming internally. The question is whether n8n consumed the response stream and converted it into the JSON representation you requested.
For the control, all four HTTP Request nodes call the same https://postman-echo.com/post endpoint. Only the body type and response-format setting change. That removes most upstream variability from the comparison.
Upstream issue #36402 reports the same symptom on n8n 2.34.5: Raw request body, explicit JSON response, successful execution, but the node output contains stream internals instead of parsed JSON. The issue was assigned to the Nodes area, which makes it materially different from a generic configuration anecdote.
Four-case control: only Raw Body + explicit JSON exposed the stream object
The workflow contains one Manual Trigger and four HTTP Request branches:

| Case | Body Content Type | Response Format | Local n8n 1.112.4 result |
|---|---|---|---|
| A | Raw | JSON | Stream internals returned |
| B | JSON | JSON | Parsed JSON |
| C | Raw | Auto-detect | Parsed JSON |
| D | Raw | Text | Text wrapper |
Case A still reports node execution success. The problem is the value shape. Its output contains:
_readableState
_writableState
bytesWritten
_handle
_outBuffer
...
That is operationally worse than a clean 400 or 500 in some workflows. Downstream expressions such as $json.id, $json.data.status, or delivery checks may simply become undefined even though the remote request itself succeeded.
Case B changes only the outgoing body type from Raw to JSON and leaves Response Format on JSON. The normal response immediately returns, including args, data, headers, json and url, with the echo marker available at json.case.
Case C keeps Raw Body but returns the response setting to Auto-detect. It also produces parsed JSON. That comparison is important: the failure is not adequately described as “Raw cannot send JSON.” The stronger hypothesis is that Raw enables a stream response, while explicit JSON skips the branch that consumes that stream.
The runnable fixture and compact result are published with the article:
Why the root cause points to useStream and autoDetectResponseFormat
The tagged [email protected] HTTP Request V3 source contains two relevant stages.

During request-option construction, Auto-detect responses and file responses enable streaming. Separately, a Raw request body also enables useStream. Reduced to the important branches, the code behaves like this:
if (autoDetectResponseFormat || responseFormat === 'file') {
requestOptions.useStream = true;
} else if (bodyContentType === 'raw') {
requestOptions.json = false;
requestOptions.useStream = true;
} else {
requestOptions.json = true;
}
After the response arrives, the code under autoDetectResponseFormat inspects Content-Type and converts JSON/text streams into strings before later parsing. The conflict appears when the user explicitly selects JSON:
Raw request body
↓
useStream = true
↓
response arrives as a stream
↓
explicit Response Format = JSON
↓
autoDetectResponseFormat = false
↓
Auto-detect stream-consumption branch is skipped
↓
stream-shaped object reaches node output
This explains two otherwise surprising observations at once: why explicitly choosing JSON does not make the response more deterministic in this configuration, and why Raw + Auto-detect succeeds in the local control.
Two tested containment paths
The control produced two practical ways to get back to a consumable response without parsing stream internals yourself. They solve different request-contract constraints, so choose based on whether the outgoing body must remain byte-for-byte Raw.
Workaround 1: if the body is JSON, use JSON Body
This is the containment I would choose first. Change:
Body Content Type: Raw
Response Format: JSON
into:
Body Content Type: JSON
Response Format: JSON
That combination passed the control and returned parsed JSON.
Do not stop at a green node. Revalidate at least three things: the Content-Type received by the remote service, any serialization differences in strings/numbers/escaping, and every downstream field that used the response.
If the remote service requires exact bytes—for example a signed payload or a format that must not be re-serialized—then this workaround may change request semantics and should not be applied blindly.
Workaround 2: if Raw is mandatory, test Auto-detect
Keeping Raw Body and switching Response Format to Auto-detect also passed this control. It works here because the response enters the branch that consumes the stream and uses Content-Type to decide whether it is JSON, text or a file.
The boundary is important. Auto-detect relies on the upstream Content-Type. An API that returns JSON as text/plain, or uses different content types for success and error responses, may not behave the same way. Treat Raw + Auto-detect as a tested containment path, not an upstream fix.
Do not build a permanent parser around _outBuffer
Once you notice that the response bytes are still present somewhere under _outBuffer or _readableState.buffer, it is tempting to add a Code node, reconstruct the Buffer and call JSON.parse().
That can be useful for diagnosis, but it is a poor production contract. You would be depending on internal serialization details of Node.js streams, axios, compression and n8n execution storage rather than the documented HTTP Request output. Those shapes can change with runtime version, response size and compression behavior.
The upstream report also notes that serializing the stream object can dramatically inflate execution data relative to the actual response. The right long-term target is to restore a normal JSON/text output at the HTTP Request node boundary.
Why this can be misdiagnosed as an upstream API failure
The node can be green. There may be no HTTP 500 and no JSON parse exception. The only visible change is that application fields disappear and stream internals replace them.
Imagine the next node checks:
$json.delivery_id
It now receives undefined. A workflow may interpret that as “delivery failed” even though the remote service already accepted the request. If the automation retries side-effecting calls—payments, messages, ticket creation, order updates—that misclassification can create duplicates. The retry policy should be designed together with the site’s n8n Error Workflow, retry and timeout guide rather than replaying the request simply because an expected field vanished.
A safer debugging sequence is:
- Confirm the remote operation and HTTP status independently.
- Check whether n8n output contains stream internals.
- Run a JSON Body + JSON Response control.
- Run a Raw Body + Auto-detect control.
- Only then decide whether to change request semantics, keep a temporary workaround, or wait for an upstream patch.
Evidence boundary: what this test proves and what it does not
The full local workflow ran on n8n 1.112.4 and produced the four results above. I also attempted to run [email protected] directly, but that release declares Node.js >=22.22 while the local machine had 22.18.0. Docker was installed, but its daemon was not running during this session. I did not bypass those runtime requirements merely to label the article “2.34.5 reproduced.”
For 2.34.5, I performed a separate source-level check against the tagged HttpRequestV3.node.ts and confirmed that the Raw Body useStream=true branch and Auto-detect stream-consumption structure are still present. Upstream issue #36402 supplies the 2.34.5 runtime report.
The precise evidence statement is therefore:
n8n 1.112.4 runtime reproduction + tagged n8n 2.34.5 source confirmation + upstream 2.34.5 runtime report.
When n8n ships an upstream change for #36402, the same four-case matrix should be rerun before removing any workaround.
Recommended production decision
If you are dealing with _readableState today, my order of operations is:

The third option in the image—manually consuming the stream—is appropriate for diagnosis or temporary verification, not as a durable production contract around
_readableStateor_outBuffer. The production goal remains restoring normal JSON/text output at the HTTP Request node boundary.
First: if the request payload is standard JSON, move to JSON Body + JSON Response and verify the exact wire payload.
Second: if Raw must stay Raw, test Auto-detect and verify Content-Type on both success and error responses.
Third: do not permanently parse _readableState or _outBuffer, and do not blindly retry side-effecting requests just because expected response fields disappeared.
Fourth: record the n8n version, Node.js version, HTTP Request typeVersion, body type and response format, then watch n8n issue #36402 for an upstream fix.
You can rerun the public minimal reproduction without an API key. It contains one trigger and four HTTP Request branches, which is enough to determine whether your environment has the same response-processing behavior.
For adjacent production issues, see n8n Webhook Production URL, Auth and 404 troubleshooting, n8n Error Workflow, retry and timeout handling, and the n8n Baserow parameter-dependency regression.
Continue through the production automation path
The workflow hub connects self-hosting, queue mode, webhooks, retries, observability and n8n implementation cases into one production-oriented learning path.
More to Explore
Topic hub →AI Engineering Weekly
Production changes, real failures, experiments and new XBSTACK assets.
DISCUSSION
Questions, verification and corrections
Sign in to comment. Every new comment is reviewed before publication; while pending, it is visible only to you and the administrator.