The Error That Sends Everyone to Google
You build the obvious workflow: a form trigger receives a PDF, a node converts it, then a Gmail node sends it to the customer. It fails with a message that reads like a bug report from someone else:
The item has no binary field 'data' (item 0)
Nothing is wrong with your logic. The problem is that you are mixing up two completely separate data channels that n8n carries in parallel, and almost every beginner assumes only one of them exists.
Two Channels: JSON and Binary
Every item flowing between n8n nodes has two halves. The JSON half holds structured, inspectable values — strings, numbers, objects, arrays. This is what you see in the JSON tab of the output panel, what expressions like {{ $json.email }} read from, and what almost every node parameter expects by default.
The binary half holds raw file bytes — PDFs, images, spreadsheets, audio. This is what lives in the separate Binary tab of the output panel. Files are too large and too opaque to be JSON values, so n8n keeps them out of the text stream entirely.
That is the whole model, and it explains every symptom:
- Why
{{ $binary.data }}does nothing: it is a binary property, not a JSON property. You cannot interpolate raw file bytes into a text field, and a Code node that returns onlyjsonsilently discards the binary half of every item. - Why the fix is a name, not a path: binary items are keyed by a property name, usually
data,file, orattachment. A node does not know your filename — it looks up that key. - Why the key changes mid-workflow: nodes like Move Binary Data, Extract from File, and HTTP nodes with "Download" enabled decide the output key. If an upstream node emits
Bericht, every downstream node must be toldBericht.
Fix 1: Match the Binary Property Name Exactly
This is the correct fix for roughly 80% of attachment failures, and it takes 30 seconds. Run the node that produces the file on its own, open the output panel, click the Binary tab, and copy the exact key name shown next to the file.
Then open your email node and set its attachment field to that exact string:
- Gmail node → Attachments Input → Binary Property: set to the copied key, e.g.
data. - Send Email node → Attachments: the same idea — the field takes the binary property name, not the filename.
- If the email node expects an array of field names (some versions do), pass
{{ Object.keys($binary).join(',') }}and it will pick up whatever keys are present on that item.
Run the producing node alone, read the Binary tab, and you never have to guess a property name again. The names in the community forum posts that leave you stuck (Bericht, image1868) are real, and they are always discoverable the same way.
Fix 2: Stop Your Code Nodes From Dropping Files
The second most common cause is a Code node in the middle of the chain. If your code builds a brand-new object and returns only json, the binary payload is gone from that point onward — and the email node fails with the exact same "no binary field" message even though the name is right.
Preserve both halves explicitly in the Run Once for All Items mode:
// ❌ drops the file — binary is gone from here on
return items.map(i => ({ json: { email: i.json.email } }));
// ✅ carries json and binary through
return items.map(i => ({
json: { email: i.json.email },
binary: i.binary
}));
Same rule for the Set and Aggregate nodes: set "Keep Only Set Fields" to off so existing binary data is retained. An Aggregate node set to All Item Data but with Include Binaries unchecked is a very common source of a one-line fix.
Fix 3: Convert the File to the Format You Actually Attach
Attaching a converted file needs two nodes, in the right order:
- Convert to File — takes a text/binary/JSON value and wraps it as a real file. It needs a Binary Property name and a File Name, e.g.
dataandinvoice.pdf. - Edit Fields (Set) or your email node — reference that same property name.
A very common source of confusion is Extract from File. That node reads a file's contents into JSON — useful for reading a CSV or invoice PDF, and the opposite of attaching. Use it when you need to parse, and convert to file when you need to send. Running the wrong one of the pair is why the attachment "disappears" and turns up as an unreadable blob of text instead.
Fix 4: Binary Storage Mode Matters on Self-Hosted n8n
Where n8n keeps those bytes is a server-level decision, and it changes how reliable attachments are — especially if you self-host, which most of this site's readers do.
- Default (in-memory): files live in the process's memory during execution and in the database between executions. n8n's own docs note that large files in memory can crash the instance. Fine for small PDFs, risky for batches.
- Filesystem: set
N8N_DEFAULT_BINARY_DATA_MODE=filesystemand files are written to disk underN8N_USER_FOLDER/binaryData(override withN8N_BINARY_DATA_STORAGE_PATH). This is the recommended mode for a single-instance self-hosted setup, and it is what keeps a 40MB upload from bloating your Postgres backups. - Database: set the mode to
databasewhen you run queue mode with multiple workers — filesystem mode is not supported in queue mode. Each file is capped byN8N_BINARY_DATA_DATABASE_MAX_FILE_SIZE, 512 MiB by default, and cannot exceed the 1024 MiB column limit. - S3 / Azure: external object storage for multi-worker production setups. External binary storage requires a self-hosted Business or Enterprise plan, and cannot be selected through
N8N_AVAILABLE_BINARY_DATA_MODESwithout that licence.
The backup consequence nobody warns you about
If you switch binary modes, n8n only prunes the mode that is currently active — and your existing backup of the database will not contain the files that live on disk in filesystem mode. If you built a workflow that attaches PDFs, add binaryData/ to the same backup job that dumps your database, or a restore will bring back every workflow with the attachments missing.
Pruning also runs against the active mode only, so leftover files from a previous mode can sit on disk indefinitely. Worth a du -sh ~/.n8n/binaryData on a self-hosted box every few weeks.
A Checklist That Prevents 95% of This Class of Bug
- Run the file-producing node alone and read the Binary tab. Never guess the key name.
- Set the email node's attachment field to that exact key — no filename, no path, no quotes.
- Any Code, Set, or Aggregate node between producer and email must carry
binarythrough. - Use
Convert to Fileto create a file; useExtract from Fileonly to parse one. Never both for the same intent. - Confirm you have a Gmail credential with send scope, and that the account's daily sending quota is not the real failure.
- On self-hosted, decide binary mode deliberately and back up whichever store you picked.
Related Reading
Binary data is where the abstractions leak. The same pairing — a trigger handing off to a downstream action — shows up in setting up n8n error handling so a failed attachment never fails silently, and in idempotency on webhooks, where the same retry that duplicates a charge also duplicates the attached invoice. Get the error path right first and the binary field name is the only thing left to get right.