fix(HTTP Request Node): Show the API response when a file upload fails - #36924
Open
BerniWittmann wants to merge 2 commits into
Open
fix(HTTP Request Node): Show the API response when a file upload fails#36924BerniWittmann wants to merge 2 commits into
BerniWittmann wants to merge 2 commits into
Conversation
A binary upload read from external binary storage stays a live stream. Once
the request is in flight, that stream's pipe chain is circular, and
`sanitizeUiMessage` only replaced Buffers, so the stream reached the browser
inside `error.context.request`. Execution data travels as `flatted`, which
restores the cycle, and interpolating it threw from Vue's `toDisplayString`.
The render error tore down the error view and left an empty output panel.
Replace upload streams in `sanitizeUiMessage` before `deepCopy` preserves the
cycle, covering `body`, a `form-data` instance, and the plain
`{ field: { value, options } }` map used below node version 4.2. Also render
sanitized copies in `NodeErrorView`, so an execution recorded before this fix
still shows its error.
Contributor
PR review overviewBased on ownership of the 4 changed files in this PR:
|
BerniWittmann
marked this pull request as ready for review
August 24, 2026 12:36
Bundle ReportChanges will increase total bundle size by 317 bytes (0.0%) ⬆️. This is within the configured threshold ✅ Detailed changes
Affected Assets, Files, and Routes:view changes for bundle: editor-ui-esmAssets Changed:
Files in
|
Contributor
There was a problem hiding this comment.
All reported issues were addressed across 4 files
Architecture diagram
sequenceDiagram
participant Store as Binary Storage (S3/Disk)
participant Node as HTTP Request Node (Backend)
participant API as External API
participant UI as Frontend (NodeErrorView)
Note over Store, API: Runtime Execution Flow (File Upload)
Node->>Store: Request binary data reference
Store-->>Node: Returns Readable Stream
Note right of Store: Stream contains circular references<br/>(res -> socket -> _httpMessage)
Node->>API: POST request (Stream as body/formData)
API-->>Node: 4xx/5xx Error Response + JSON Body
Note over Node: Error Preparation & Sanitization
Node->>Node: Catch error and extract request metadata
Node->>Node: CHANGED: Run sanitizeUiMessage()
alt Body is Stream or Large Buffer
Node->>Node: NEW: Replace with "Binary data got replaced..." string
else Multipart (formData) contains Stream
Node->>Node: NEW: Recursively replace nested Streams with placeholder
end
Node->>Node: Redact sensitive auth/headers
Node-->>UI: Transport Execution Error (via flatted/JSON)
Note over UI: UI Rendering (Node Details View)
UI->>UI: NEW: map context/extra through replaceCircularReferences()
Note right of UI: toRaw() used to skip reactive proxy overhead
alt Rendering Error Details
UI->>UI: Vue calls toDisplayString (JSON.stringify)
Note right of UI: No longer throws TypeError on circularity
UI-->>UI: Display sanitized Request and API Response
end
UI-->>UI: Render "Bad request" toast + Error Body panel
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
…ng them `Object.entries` surfaces `__proto__` as an own key on anything `jsonParse` builds, and assigning to that key runs the inherited setter: the key was dropped from the copy and the throwaway object's prototype was retargeted instead. `body` skips `deepCopy`, so it carried that out and lost the key from the request shown in the error details. `Object.fromEntries` defines each key rather than assigning it, restoring the behaviour this branch had before the upload substitution went in.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
An HTTP Request node that POSTs a file and gets a 4xx back rendered an empty output panel. The toast showed
Bad request - please check your parameters, but the API's actual response was nowhere to be seen.The response body was never lost — the error view crashed while rendering.
filesystem/s3, the cloud default) stays a liveReadable, not aBuffer.sanitizeUiMessageonly swapped the body out when it was aBufferover 250 KB, so the stream passed through intoerror.context.request._readableState.pipeschain is circular (res → socket → _httpMessage).flatted, which faithfully restores the cycle in the browser.NodeErrorViewinterpolated the value. Vue'stoDisplayStringcallsJSON.stringify, which threw, and the render error tore down the whole error view.This is why it only appeared with real files: a small inline binary is a serializable
Buffer, a stored one is a stream.The fix has two halves.
sanitizeUiMessagenow replaces upload streams beforedeepCopypreserves the cycle — coveringbody, aform-datainstance (node version 4.2+), and the plain{ field: { value, options } }map used below 4.2 and in V1/V2.NodeErrorViewrenders sanitized copies, so an execution already recorded before this fix still shows its error after an upgrade.OUTPUT ⚠+1 item, blank bodyBefore:
After:
How to test
The bug needs binary data stored outside the run data — with the default binary mode the upload is an inline
Buffer, which was always serializable, and nothing reproduces.1. Save the mock API below as
mock-server.mjsand start it:mock-server.mjs2. Start n8n with binary data stored on disk:
3. Import the workflow below, run it, then open the Upload PDF to Files API node.
Example workflow
{ "name": "NODE-5292 repro: POST binary upload error", "nodes": [ { "parameters": {}, "type": "n8n-nodes-base.manualTrigger", "typeVersion": 1, "position": [0, 0], "id": "11111111-1111-1111-1111-111111111111", "name": "When clicking 'Execute workflow'" }, { "parameters": { "url": "http://127.0.0.1:4599/big.pdf", "options": { "response": { "response": { "fullResponse": true, "responseFormat": "file" } } } }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [220, 0], "id": "22222222-2222-2222-2222-222222222222", "name": "Fetch example PDF file" }, { "parameters": { "method": "POST", "url": "http://127.0.0.1:4599/v1/files", "sendBody": true, "contentType": "binaryData", "inputDataFieldName": "data", "options": { "response": { "response": { "fullResponse": true } } } }, "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.4, "position": [440, 0], "id": "33333333-3333-3333-3333-333333333333", "name": "Upload PDF to Files API" } ], "pinData": {}, "connections": { "When clicking 'Execute workflow'": { "main": [[{ "node": "Fetch example PDF file", "type": "main", "index": 0 }]] }, "Fetch example PDF file": { "main": [[{ "node": "Upload PDF to Files API", "type": "main", "index": 0 }]] } }, "active": false, "settings": { "executionOrder": "v1" } }Expected: the output panel shows the error box with
file exceeds the maximum allowed size. Expand Error details → From HTTP Request and confirmRequest.bodyreadsBinary data got replaced with this text. Original was a stream.The browser console must stay clean.To see the bug, check out
masterand repeat step 3: the panel shows onlyOUTPUT ⚠and1 item, and the console logsConverting circular structure to JSONfromtoDisplayString.Also worth checking (multipart, the other upload shape): set Body Content Type to
Form-Data, add a parameter of typen8n Binary Filewith input fielddata, and point it at the same URL. The panel must render its error too.No regressions in the request preview: open any normal HTTP Request node and confirm the request shown under Error details is unchanged for JSON, string, and form-urlencoded bodies.
Related Linear tickets, Github issues, and Community forum posts
https://linear.app/n8n/issue/NODE-5292
Review / Merge checklist
Backport to Beta,Backport to Stable, orBackport to v1(if the PR is an urgent fix that needs to be backported)🤖 PR Summary generated by AI