Email attachments not sending via SMTP node in n8n 2.3.0 (self-hosted)

Describe the problem/error/question

Hi,
I’m trying to send emails with attachments from IMAP trigger to SMTP node in my self-hosted n8n 2.3.0 setup.
Setup:

  • Email Trigger (IMAP) with “Simple” format enabled
  • N8N_DEFAULT_BINARY_DATA_MODE=default
  • Send Email node (SMTP) with Plesk mail server
    The Problem:
    Emails send successfully but attachments are NOT included. When I debug, I see the attachments exist as binary data, but they don’t appear in the received email.
    I’ve tried:
    :white_check_mark: Changed N8N_DEFAULT_BINARY_DATA_MODE to “memory” and “default”
    :white_check_mark: Restarted with docker compose down -v
    :white_check_mark: Used “Extract from File” node to convert to Base64
    :white_check_mark: Set Binary Properties to “attachment_0,attachment_1,attachment_2”
    None of these worked. The email arrives but attachments are always missing.
    Questions:
  1. Is this a known bug with SMTP + binary data in 2.3.0?
  2. Are there any workarounds besides using Gmail API?
  3. Should I downgrade to n8n 1.x?
    Any help would be appreciated!

If i had debug in html mail {{ JSON.stringify($(‘Email Trigger (IMAP)’).item.binary, null, 2) }}
i can see that :slight_smile:

{ "attachment_0": { "mimeType": "image/webp", "fileType": "image", "fileExtension": "webp", "data": "UklGRoKUAABXRUJQVlA4WAoAAAAgAAAArwQAogIASUNDUEgMAAAAAAxITGlubwIQAABtbnRyUkdCIFhZWiAHzgACAAkABgAxAABhY3NwTVNGVAAAAABJRUMgc1JHQgAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLUhQICAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFjcHJ0AAABUAAAADNkZXNjAAABhAAAAGx3dHB0AAAB8AAAABRia3B0AAACBAAAABRyWFlaAAACGAAAABRnWFlaAAACLAAAABRiWFlaAAACQAAAABRkbW5kAAACVAAAAHBkbWRkAAACxAAAAIh2dWVkAAADTAAAAIZ2aWV3AAAD1AAAACRsdW1pAAAD+AAAABRtZWFzAAAEDAAAACR0ZWNoAAAEMAAAAAxyVFJDAAAEPAAACAxnVFJDAAAEPAAACAxiVFJDAAAEPAAACAx0ZXh0AAAAAENvcHlyaWdodCAoYykgMTk5OCBIZXdsZXR0LVBhY2thcmQgQ29tcGFueQAAZGVzYw....

What is the error message (if any)?

Please share your workflow

Share the output returned by the last node

Information on your n8n setup

  • n8n version: 2.3.0
  • Database (default: SQLite): postgres
  • n8n EXECUTIONS_PROCESS setting (default: own, main):
  • Running n8n via (Docker, npm, n8n cloud, desktop app): docker
  • Operating system: ubuntu

Can you post a screenshot of how the Attachments part of the SMTP node is set up? The Binary Property field needs to match the property name exactly, comma separated, no spaces. Something like attachment_0,attachment_1 works fine, but if there’s a space after the comma it can break silently.

That binary data mode setting is what stands out to me most. When it’s on “default” it saves the attachment to disk and grabs it again later when it’s time to send the email. If your setup has more than one container involved anywhere, the SMTP node might just not be able to find that file when it goes to attach it and it won’t throw an error, it’ll just quietly skip it. Which matches exactly what you’re seeing. Also, that docker compose down -v you ran the -v wipes volumes. If your binary storage was sitting in one of those, that command might’ve deleted it right there.

Hi, thanks for your help

Hi @Anthony_P Welcome!
Nothing binary reaches Send email. formatInfoMail returns a plain json object with no binary key, and Message a model replaces the item again after it, so the properties your Attachments expression names no longer exist on the item by the time the node runs.
Put the binary back with a Code node between si humain and Send email:

return [{
  json: $input.first().json,
  binary: $('Email Trigger (IMAP)').first().binary,
}];

Then set Attachments to {{ Object.keys($binary).join(',') }}.

Downgrading will not help, pointing that field straight at another node’s binary is an open issue last reported on 1.115.3:

change the join to not add a space

{{ Object.keys($('Email Trigger (IMAP)').item.binary).join(',') }}

Thanks for the explanation! But I need to clarify something: When I add this debug in the email body: {{ JSON.stringify($(‘Email Trigger (IMAP)’).item.binary, null, 2) }} I can see the full Base64 content of the attachments. This proves the binary data still exists and is accessible from the Email Trigger node. If the binaries were truly lost like you mentioned, I wouldn’t see anything in the debug output. But I’m seeing the complete file data (Base64 encoded). So the issue is NOT that the binaries disappear - they’re clearly there. The problem seems to be that the Send email node’s “Binary Properties” field can’t access them from the Email Trigger node directly.

I’ve already tested this many times - both with the full node path expression and just putting attachment_0,attachment_1 directly - but nothing works. What’s strange is: when I debug by adding {{ JSON.stringify($(‘Email Trigger (IMAP)’).item.binary, null, 2) }} in the email body, I can see the full content of the binary object with all the Base64 data. This proves the attachments exist and are accessible. But the Send email node won’t attach them.

We have taken a look at this and it looks like it might be fixed in a recent release, Please update and see if you are still running into the same problem.

That debug output is an expression reading the IMAP node’s data directly, which works from anywhere in the workflow and does not put anything on the item. Attachments is not an expression that fetches data, it is a list of property names the node looks up on its own input item, and your screenshot previews it resolving to exactly the right three names. The node then looks for attachment_0 on the item handed over by si humain, finds nothing, and sends without it.
Both of your tests changed that string. No value in that field can work until something puts the binary onto the item, which is what the Code node does. An upgrade will not change that either.

Thanks, that works. However, I’m still struggling to understand why I have to rebuild the object before sending the email.
It is almost certainly because, between the moment I retrieve the attachments and the moment the email is sent, there are intermediate blocks—and the binary data from the first block doesn’t automatically pass from one block to the next. In the online tutorials I’ve seen, the attachment is defined in the preceding block.

Binary is not inherited, every node builds its own output item and decides whether to carry it over. formatInfoMail returns a plain object, so the binary was gone one node after the trigger, and Message a model builds a fresh item of its own. The two IF nodes hand the item straight through, so the number of nodes in between was never the thing.
That is also why the tutorials need no rebuild, the attachment sits on the output of the node immediately before Send email. Nodes that reshape data and can keep it expose the choice in their options, Edit Fields and Split Out both have a setting for whether binary carries into their output.