n8n - 💡(How to fix) Fix bug(nodes): HTTP Request node assigns last item's filename to all binary outputs when downloading files in parallel [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
n8n-io/n8n#30419Fetched 2026-05-14 03:45:20
View on GitHub
Comments
1
Participants
2
Timeline
4
Reactions
0
Timeline (top)
commented ×1labeled ×1mentioned ×1subscribed ×1

Root Cause

Root Cause Analysis

Code Example

{
  "parameters": {
    "url": "={{ $json.file.url_private_download }}",
    "authentication": "predefinedCredentialType",
    "nodeCredentialType": "slackApi",
    "options": {}
  },
  "type": "n8n-nodes-base.httpRequest",
  "typeVersion": 4.3
}

---

// packages/nodes-base/nodes/HttpRequest/V3/utils/binaryData.ts
export const setFilename = (preparedBinaryData, requestOptions, responseFileName) => {
  if (!preparedBinaryData.fileName && ...) {
    return requestOptions.uri.split('/').pop(); // ← always last item's URI!
  }
  return preparedBinaryData.fileName;
};

---

// Before:
preparedBinaryData.fileName = setFilename(
    preparedBinaryData,
    requestOptions,              // ← always the last item's value after the loop
    responseFileName,
);

// After:
preparedBinaryData.fileName = setFilename(
    preparedBinaryData,
    requests[itemIndex].options, // ← correct per-item request options
    responseFileName,
);

---

content-disposition: attachment; filename="Invoice_ 830704_Jan2026.pdf"; filename*=UTF-8''Invoice_%20830704_Jan2026.pdf
RAW_BUFFERClick to expand / collapse

Bug Description

When using the HTTP Request node to download files with multiple input items (parallel execution), all output binary items receive the filename of the last item instead of their own correct filename.

Tested with Slack url_private_download URLs (authenticated, with redirect to CloudFront/S3). Downloading 3 different PDF files: all 3 output binary items end up with the filename of the 3rd item.

Downloading each file in isolation (one item at a time) produces the correct filename for each.

To Reproduce

  1. Create a workflow where 3 items flow into an HTTP Request node
  2. Each item has a different Slack file URL ({{ $json.file.url_private_download }})
  3. Use the HTTP Request node with Slack API authentication and default options (autodetect response format)
  4. Run the workflow — all 3 output binary items will have the filename of the last item

Minimal node config:

{
  "parameters": {
    "url": "={{ $json.file.url_private_download }}",
    "authentication": "predefinedCredentialType",
    "nodeCredentialType": "slackApi",
    "options": {}
  },
  "type": "n8n-nodes-base.httpRequest",
  "typeVersion": 4.3
}

Expected behavior

Each output item should have its own correct filename, matching the file that was downloaded for that input item.

Root Cause Analysis

The shared requestOptions variable (confirmed bug)

In packages/nodes-base/nodes/HttpRequest/V3/HttpRequestV3.node.ts, the requestOptions variable is declared as a function-level let (line 120) and reassigned in the request-building loop for each item (line 357). After the loop completes, it holds the value from the last iteration.

The response-processing loop (line 831+) then calls setFilename() using this shared requestOptions (line 1015) instead of the per-item requests[itemIndex].options. Since setFilename() extracts the filename from requestOptions.uri, all items receive the URI-based filename of the last item.

// packages/nodes-base/nodes/HttpRequest/V3/utils/binaryData.ts
export const setFilename = (preparedBinaryData, requestOptions, responseFileName) => {
  if (!preparedBinaryData.fileName && ...) {
    return requestOptions.uri.split('/').pop(); // ← always last item's URI!
  }
  return preparedBinaryData.fileName;
};

Fix: Replace requestOptions with requests[itemIndex].options at line 1015:

// Before:
preparedBinaryData.fileName = setFilename(
    preparedBinaryData,
    requestOptions,              // ← always the last item's value after the loop
    responseFileName,
);

// After:
preparedBinaryData.fileName = setFilename(
    preparedBinaryData,
    requests[itemIndex].options, // ← correct per-item request options
    responseFileName,
);

Why the bug only manifests with certain URLs (e.g. Slack, not direct image links)

setFilename() only falls back to requestOptions.uri when preparedBinaryData.fileName is not set. prepareBinaryData() sets fileName via two paths:

  1. Content-Disposition headerbinaryData.contentDisposition?.filename
  2. Response URLnew URL(responseUrl).pathname or binaryData.req?.path

For direct image URLs (no redirect, no auth): responseUrl or req.path reliably resolves to a per-item path (e.g. /images/cat.jpg) → fileName is set → setFilename returns it correctly, the shared requestOptions bug is never reached.

For Slack url_private_download (authenticated + redirect to CloudFront/S3): something in the redirect/auth chain causes prepareBinaryData to not resolve a valid fileName from either path. The Slack response does include a correct Content-Disposition header per file:

content-disposition: attachment; filename="Invoice_ 830704_Jan2026.pdf"; filename*=UTF-8''Invoice_%20830704_Jan2026.pdf

Yet the filename ends up URL-based (invoice__830704_jan2026.pdf style), indicating the Content-Disposition path in prepareBinaryData is not being taken. This may be due to the instanceof IncomingMessage check failing in certain authentication/redirect execution paths.

So the shared requestOptions fix is necessary, but there may also be a secondary issue with Content-Disposition-based filename extraction for authenticated/redirected requests.

Debug Info

n8n version: 2.20.6 Node typeVersion: 4.3

Environment

  • Operating System: Linux (self-hosted)
  • n8n Version: 2.20.6
  • Node.js Version: 22.x
  • Database: PostgreSQL

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

Each output item should have its own correct filename, matching the file that was downloaded for that input item.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING