HTTP Request node fails with "Converting circular structure to JSON"

I am trying to build a workflow that downloads files from Slack and uploads them to Redmine.

The workflow contains two HTTP Request nodes:
DownloadFileFromSlackDownloads the file successfully from Slack
UploadFilesToRedmineIntended to upload the downloaded file to Redmine

The problem occurs when I pass the output of DownloadFileFromSlack directly into UploadFilesToRedmine.

The workflow execution fails with the following error:

Workflow execution had an error Error: 
Converting circular structure to JSON 
--> starting at object with constructor 'Object' 
| property 'res' -> object with constructor 'Object' 
| property 'socket' -> object with constructor 'Object' 
--- property '_httpMessage' closes the circle

Problem executing workflow 
There was a problem executing the workflow. 
Error: Converting circular structure to JSON 
--> starting at object with constructor 'Object' 
| property 'res' -> object with constructor 'Object' 
| property 'socket' -> object with constructor 'Object' 
--- property '_httpMessage' closes the cir

Expected behavior:

The binary file downloaded from Slack should be passed to the second HTTP Request node and uploaded to Redmine without serialization errors.

Actual behavior:

The workflow crashes before the upload request is executed.

Notes:

  • File download from Slack works correctly.
  • The issue appears only when forwarding the result into another HTTP Request node.

Workflow JSON:

Can you try this?

1. Change the Response Format

  1. Open the DownloadFileFromSlack node.
  2. Look for the Response section.
  3. Change the Response Format from JSON (or whatever it is currently) to File.

2. Match the Binary Property Name

  1. In the DownloadFileFromSlack node, check the Put Output File in Field setting. By default, this is usually called data.
  2. Now, look at your UploadFilesToRedmine node. You have inputDataFieldName set to data.
  3. Ensure these two names match exactly. If the first node saves the file as data, the second node must look for data.

I tested both Autodetect and File as the Response Format in the DownloadFileFromSlack node, but the same error still occurs.

Regarding the binary property name — yes, they already match.

In DownloadFileFromSlack, the file is stored in the binary field named data.

In UploadFilesToRedmine, I use:

  • Send Body = enabled
  • Body Content Type = n8n Binary File
  • Input Data Field Name = data

So the second node is explicitly reading the same binary field produced by the first node.

Can you

  1. Run only the DownloadFileFromSlack node and inspect the output item’s structure.

  2. Confirm the binary data exists

Yes - I checked this more carefully.

The binary file is definitely present in the output of DownloadFileFromSlack.

Also, if I disable Send Body in UploadFilesToRedmine, the workflow executes successfully, and I get a normal Redmine API error due to the missing file payload, which is expected.

So the issue is very likely not with the Slack download step or missing data, but specifically with how the binary file is being handled/serialized when it is passed into the UploadFilesToRedmine HTTP Request node in n8n Binary File mode.

For additional context, this might be relevant:

I am running n8n in a Kubernetes cluster in HA mode with EXECUTIONS_MODE=queue.

Setup:

  • 1x main instance (n8n start)

  • 2x webhook instances (n8n webhook)

  • 2x worker instances (n8n worker)

Not sure if this could influence how binary data is handled across nodes, but I’m mentioning it in case it helps narrow down the issue.

Hello, I had the same issue in one of my workflows.

This is how I had resolved the thing:

  1. Instead of “n8n binary file”, I used “Form-Data” in “Body Content Type”
  2. In “Body”, as “Type”, “n8n binary File”

Don’t know why it worked, but it worked.

Hope it will work for you

Welcome to the community, @DmitryMigunov! Interesting case, and glad @jmoura found a fix.

Here’s why the Form-Data approach works: when you set Body Content Type to “n8n Binary File” directly (not wrapped in Form-Data), n8n tries to serialize the entire item including the internal metadata attached to binary data, which contains circular references like res -> socket -> _httpMessage that JSON.stringify can’t handle. That’s exactly what the error is pointing to.

When you switch to Form-Data with the field type set to n8n Binary File, n8n instead handles the binary as a proper multipart stream attachment, bypassing the JSON serialization entirely. The file bytes get sent as-is rather than going through any JSON encoding step, so the circular reference never triggers.

The Redmine API for uploading files expects a raw binary upload or multipart form-data anyway, so Form-Data is actually the more correct approach for this use case.

@DmitryMigunov if jmoura’s solution worked for you, it’d be great if you could mark it as the Solution so others running into this Slack-to-Redmine pattern can find the answer quickly!

Thanks - I tried this approach.

However, I’m getting an 406 error from the Redmine API, which suggests that the request structure is still not what Redmine expects

From the Redmine Docs:

POST /uploads.json?filename=image.png
Content-Type: application/octet-stream
...
(request body is the file content)

I managed to fix it.

The issue was caused by using the wrong body mode for the Redmine upload endpoint.

The working configuration is:

  • Body Content Type = Raw

  • Content-Type header = application/octet-stream

  • Body = data (binary property)

Update: unfortunately, this still doesn’t fully solve the issue.

Even though the request now matches the Redmine documentation more closely (Body Content Type = Raw, Content-Type: application/octet-stream, and sending the binary data), the files are still coming through as corrupted on the Redmine side.

So it seems the request format is correct in principle, but the actual binary payload being sent via n8n is not being preserved properly when using the Raw body mode.

good day @DmitryMigunov

Thanks for the updates. Based on what you tested, I don’t think this is a binary field name issue anymore. The file exists as binary data, and the Redmine request format seems closer now, but the corruption suggests the bytes may not be preserved when sent through Raw body mode.

Since you are running n8n in queue/HA mode, I would also check your binary data storage. If binary data is stored on local filesystem, all workers/webhook/main pods need access to the same persistent storage, otherwise binary handling can become unreliable across executions.

As a workaround, I would avoid sending the binary through a Raw body expression. I would either upload it from the same execution context with a Code node or store the file temporarily in shared storage like S3/MinIO, then upload the real file bytes from there to Redmine.

Just to clarify — when you suggest using a Code node for uploading, do you mean something like using this.helpers.requestWithAuthentication inside the Code node?

const files = $('Slack Trigger').first().json.files || [];
const results = [];

for (const file of files) {
    const response = await this.helpers.requestWithAuthentication('slackApi', {
      method: 'GET',
      url: file.url_private,
      encoding: null,
      resolveWithFullResponse: true,
    }, 'Slack account');

    const fileBuffer = response.body;

    const uploadResponse = await this.helpers.requestWithAuthentication('genericCredentialType', {
      method: 'POST',
      url: `https://redmine.example.com/uploads.json?filename=${encodeURIComponent(file.name)}`,
      headers: {
        'Content-Type': 'application/octet-stream',
      },
      body: fileBuffer,
      json: true,
    }, 'Redmine Admin');

    results.push({
      token: uploadResponse.upload.token,
      filename: file.name,
      content_type: file.mimetype
    });
}


return [{
  json: {
    attachments_array: results
  }
}];

I tried that approach, but I ran into an issue where this.helpers.requestWithAuthentication is not available:

this.helpers.requestWithAuthentication is not a function [line 5] (TypeError)

So I assume I might be using the wrong execution context or API, or this helper is not exposed in Code nodes anymore.

Could you confirm the recommended way to perform an authenticated binary upload from a Code node in the current version?

@DmitryMigunov

Yes, that is the general idea, but I think the specific helper name is the issue here.

I would not use this.helpers.requestWithAuthentication in the Code node. That helper is not available there in that form. The helper exposed in n8n node development is httpRequestWithAuthentication, and the older request helper was deprecated.That said, for this case I would still avoid putting the whole upload logic in a Code node if possible, because credential access from Code nodes can be limited depending on context and version.

My safer approach would be to split it into two steps. First, use a Slack HTTP Request or Slack node to download the file as binary. Then use an HTTP Request node for Redmine, with authentication configured in the node itself, and send the body as the real binary file with application/octet-stream. If the HTTP Request node keeps corrupting the file, then I would move this upload to a very small external helper service instead of the Code node. The helper service would receive the Slack file URL and filename, stream the bytes directly to Redmine, and return the Redmine upload token back to n8n.

@DmitryMigunov Totally get the confusion here! @tamy.santos nailed it. The this.helpers.requestWithAuthentication API is only available inside custom node development (TypeScript), not inside the n8n Code node at runtime.

The cleanest approach for your Slack-to-Redmine file upload without a Code node would be:

Step 1 (HTTP Request node): Download the file from Slack

  • URL: {{ $json.url_private }}
  • Authentication: use your Slack credential
  • Response format: File
  • This gives you a proper binary output

Step 2 (HTTP Request node): Upload to Redmine

  • Method: POST
  • URL: https://your-redmine.com/uploads.json?filename={{ $json.name }}
  • Authentication: your Redmine API key in Header
  • Body: send the binary data from Step 1 as application/octet-stream

The key thing is Step 1 must output a binary/file not JSON, then Step 2 picks it up directly. No Code node needed, and no circular structure issues at all.

If you want to try this and it works for you, feel free to mark the most useful reply as Solution so future searchers can find it!

I believe I followed the approach exactly as described.

However, in Redmine the resulting file is empty (0 bytes), so it seems the binary is not being correctly transferred during the upload step.

As a side note, I also switched N8N_DEFAULT_BINARY_DATA_MODE to database, so binary storage across workers should not be an issue anymore.

At this point it looks like the binary data is available in the workflow, but something is going wrong specifically during the HTTP upload to Redmine.

@DmitryMigunov

for the Redmine upload endpoint, I would make sure the request matches the Redmine format exactly: POST /uploads.json?filename=your-file-name.jpg
Content-Type: application/octet-stream
request body should be the raw file bytes

also, from your screenshot, I see that the filename query parameter has this error: Referenced node doesn’t exist. So the expression is not resolving correctly. I would fix that first. If the file name is already in the current item, use something like: {{ $json.file.name }} or reference the actual existing node name exactly as it appears in your workflow.

@DmitryMigunov Nice debugging work tracking this down step by step!

The 0-byte file issue is a classic Redmine upload gotcha. Redmine’s /uploads.json endpoint strictly requires raw binary data in the body (not multipart, not form-encoded). When n8n sends the body using Raw mode, you need to make sure the body value points directly to the binary buffer, not just the binary metadata.

Here’s the exact setup that should work:

HTTP Request node (Upload to Redmine):

  • Method: POST
  • URL: https://your-redmine.com/uploads.json
  • Send Query Parameters: filename = {{ $binary.data.fileName }} (use the actual binary property name, e.g. data)
  • Send Headers: Content-Type = application/octet-stream
  • Send Body: ON
  • Body Content Type: n8n Binary File
  • Input Data Field Name: data (whatever the binary property is called in your previous node output, check the orange label in the node output panel)

The critical part is using n8n Binary File as the body type, NOT Raw. With Raw mode, n8n tries to serialize the binary reference as JSON, which results in an empty or corrupted payload. The n8n Binary File option streams the actual binary buffer directly.

Also double-check the Input Data Field Name matches the orange-labeled property name in the Slack download node output. If it says data, use data. If it says something else, use that exact name.

nguyenthieutoan nailed it. the n8n Binary File body type is the fix, not Raw.

one thing to add — if you’re still getting the “Referenced node doesn’t exist” error on the filename parameter, don’t use an expression there at all for now. just hardcode a filename like test.jpg to isolate the upload issue first. once the file is going through with actual bytes, then go back and wire up the dynamic filename. debugging two things at once is what’s making this harder than it needs to be.

@nguyenthieutoan thanks — and yes, using {{ $binary.data.fileName }} works correctly for me as well, so the filename part is resolved :+1:

However, the main issue is still present on my side.

In DownloadFileFromSlack I have:

  • Response Format: File

  • Put Output in Field: data

The binary output looks correct in the node output.

But as soon as I configure UploadFilesToRedmine like this:

  • Body Content Type: n8n Binary File

  • Input Data Field Name: data (set as a fixed value, not an expression)

The execution immediately fails with:
Converting circular structure to JSON

So it seems the issue is triggered specifically when the HTTP Request node tries to process the binary input in n8n Binary File mode, rather than the binary field naming or the Redmine endpoint itself.

At this point, it feels like the binary data is valid up to the node input, but something inside the HTTP Request node execution (queue/worker context) is causing the serialization error.