Clean a content calendar CSV in n8n without losing campaign IDs
- For
- Solo marketers and small content teams who self-host n8n
- Problem
- Tidy an exported content calendar CSV while every campaign ID stays exactly as written
- Tested with
- n8n 2.42.7, self-hosted with npm, on Node.js 24.21.0
- Tested on
- Data
- synthetic test files only
This workflow takes one content calendar CSV and writes a cleaned copy. It
trims stray spaces and removes blank rows. In the CSV it writes, every
campaign ID stays exactly as written, so 0042, 42 and CAFÉ-01 remain
distinct. If the file has a problem the workflow cannot fix safely, such as
two rows with the same campaign ID, it writes an error report instead of a
cleaned CSV.
Tested: n8n 2.42.7, self-hosted with npm on Node.js 24.21.0, in the n8n editor, on 2026-10-13, with synthetic files. A script, not a person, operated the editor in a headless browser.
Not tested: Docker, n8n Cloud, Windows, macOS, other n8n versions, desktop browsers, an instance that already has workflows, and real calendar exports.
Before you start
- A self-hosted n8n that you can open in a browser. We tested only n8n 2.42.7 installed with npm. We signed in as the owner of a new instance.
- Access to the files of the machine or container that runs n8n. You need it to put your CSV where n8n may read it (Step 1). The workflow reads and writes files there, not on the computer with your browser. You can download the result through the editor (Step 5).
- Not tested on n8n Cloud. The n8n documentation says that on Cloud, files written by this node are not guaranteed to persist (S2).
- The workflow file
p1-csv-calendar-cleanup.workflow.json. It is in then8nfolder of the free sample kit. That copy differs from the tested file only in its note text and two comment lines, and it was not imported or run. - Your CSV, UTF-8, comma-separated, with a header row that contains
campaign_id,publish_date,channelandtitle. Other columns are kept.
Step 1: Put the CSV where n8n may read it
Since n8n 2.0, the Read/Write Files from Disk node may only use the
.n8n-files folder in the home folder of the user that runs n8n, unless
the administrator sets N8N_RESTRICT_FILE_ACCESS_TO (S2,
S3). n8n does not create that folder for you.
On the machine that runs n8n, signed in as the user that runs n8n:
mkdir -p ~/.n8n-files/p1/input ~/.n8n-files/p1/output
cp calendar.csv ~/.n8n-files/p1/input/
The output folder must already exist, and n8n must be allowed to write to it.
In our test, n8n ran with the home folder /home/node, so the paths were
/home/node/.n8n-files/p1/input/<file>.csv and
/home/node/.n8n-files/p1/output. A file outside that folder was refused
(see Troubleshooting).
If n8n runs in Docker, paths refer to the container's file system. Mount a
folder from your computer into the container to share files with it
(S2). We did not test Docker, so check where .n8n-files is
inside your container.
Step 2: Import the workflow
- Open a new, empty workflow. In a new instance, the Overview page offers a Build a workflow card. We tested only a new instance; with existing workflows the entry point may look different.
- Open the workflow menu (… next to the workflow name), point to
Import, choose From file, and pick
p1-csv-calendar-cleanup.workflow.json.
The canvas then shows the workflow "P1 Clean content calendar CSV (contract 0.2.0)": eight nodes and a note.
In n8n 2.42.7 the workflow saved itself; there was no Save button. We did not click Publish; manual runs worked without it. The n8n documentation names the menu item "Import from File" (S1). The tested version shows Import, then From file.
Step 3: Set the two paths
- Double-click the File paths node.
- Set
input_fileto the full path of your CSV, for example/home/node/.n8n-files/p1/input/calendar.csv. - Set
output_folderto the existing output folder, for example/home/node/.n8n-files/p1/output. - Press Tab to leave the field, then close the panel with × at the top right.
Use a plain absolute path. This field treats *, ?, [ ] and { } as
pattern characters, so the path must not contain them (S2).
In our scripted run, closing the panel right after typing kept only the /
of the last value; pressing Tab first avoided this.
After an earlier run, the OUTPUT side of this panel keeps showing that run's values until you run the workflow again. This is expected.
Step 4: Run it
Click Execute workflow at the bottom of the canvas. One of three things happens:
- The file is accepted. The nodes up to Input rejected? turn green
and n8n shows "Workflow executed successfully". One new file appears in
output_folder, named<file name without .csv>.cleaned.<UTC time>-e<execution ID>.csv. - The file is rejected (a data problem). The run stops at Stop: input
rejected. n8n shows "Problem in node 'Stop: input rejected'" with a
message such as "Input rejected (E_DUPLICATE_ID, E_DUPLICATE_ID). No
cleaned CSV was written. See …errors…json". The report
<file name without .csv>.errors.<UTC time>-e<execution ID>.jsonlists each problem with its record numbers. No cleaned CSV is written. - The workflow could not read the file (a setup problem). The run stops at Read calendar file, and n8n shows "Problem in node 'Read calendar file'". Nothing is written. See Troubleshooting.
Each run writes a new file with its own name, so earlier results are not
overwritten. The number after -e is the execution ID. The Executions
tab shows the same ID (for example "ID#5") for the selected execution.
Step 5: Download the result
Right after the run, with the workflow still open:
- Double-click the Write output file node.
- In the OUTPUT panel on the right, select Binary if it is not already selected. The card shows the file name, type and size.
- Click Download in that OUTPUT panel. The INPUT panel on the left shows the same file with its own Download button; we tested the one in the OUTPUT panel.
- Close the panel with ×.
Your browser receives the file under the name it has in output_folder,
for example valid_basic.cleaned.20261013T043039Z-e1.csv. Where the file
is saved depends on your browser's settings. We tested in a headless
browser, which has no download bar and no "Save as" dialog.
This also works for a rejected file. The run stops at Stop: input
rejected, but Write output file has already written the error report.
Its OUTPUT panel offers the .errors…json report for download.
The download is the copy of the file that n8n stored with the run. In our
test, each downloaded file was byte-for-byte identical to the file written
to output_folder, and passed our contract checker. The file also stays in
output_folder on the machine that runs n8n.
If the run stopped at Read calendar file, there is nothing to download: Write output file did not run.
We tested Download only right after each run, in the same editor session. We did not test downloading from an older execution in the Executions tab, after reloading the page, or with the View button.
What our test run produced
| Input (synthetic) | Execution | What happened | Written to output_folder and downloaded |
|---|---|---|---|
valid_basic.csv |
1 | Success | valid_basic.cleaned.20261013T043039Z-e1.csv (377 bytes), equal to the expected output |
valid_multiline.csv (line breaks inside cells) |
2 | Success | valid_multiline.cleaned.20261013T043049Z-e2.csv (312 bytes), equal to the expected output |
duplicate_ids.csv |
3 | Rejected at "Stop: input rejected" | duplicate_ids.errors.20261013T043058Z-e3.json (627 bytes; IDs 0101 and 0102 each appear twice); no CSV |
| A path to a file that does not exist | 4 | Error in "Read calendar file": "No file(s) found" | Nothing |
A file outside ~/.n8n-files |
5 | Error in "Read calendar file": "Access to the file is not allowed." | Nothing |
For example, the row 42,2026-11-03, email ,Weekly notes ,team-b
became 42,2026-11-03,email,Weekly notes,team-b, and 0042 stayed
0042.
A separate checker script, which is not part of the workflow, judged the three written files and the three downloads against the contract. All six passed.
Troubleshooting
There are two kinds of failure.
- The workflow could not run on your file. These are setup problems. The run stops before the cleanup step and writes nothing.
- The workflow ran and rejected your file. These are data problems. The run writes an error report but no CSV.
| Message | Meaning | What to do |
|---|---|---|
| "Problem in node 'Read calendar file'" with "No file(s) found" | input_file does not point to an existing file. Nothing was written. |
Check the path, file name and folder. |
| "Problem in node 'Read calendar file'" with "Access to the file is not allowed." | The file is outside the folder n8n allows (~/.n8n-files by default). Nothing was written. |
Move the file into ~/.n8n-files. Widening N8N_RESTRICT_FILE_ACCESS_TO lets file nodes in every workflow on the instance reach more of the machine, so treat that as a security decision. |
| "Set input_file and output_folder in the "File paths" node." * | A path is empty, or the output folder is only /. |
Fill in both values and press Tab before closing the panel. |
| "Expected exactly one input file…" * | The path matched several files, usually because of a pattern character. | Use one plain file path. |
| "Problem in node 'Stop: input rejected'" with "Input rejected (E_…)" | The CSV broke a rule. The .errors…json file shows which rule and where. |
Fix the source data, export it again and run the workflow again. The workflow removes only blank rows and never drops campaign records or changes their IDs to make a file pass. |
| An "n8n Assistant" page appears after you create the account | Part of first-run setup in n8n 2.42.7. | To skip it, select Set up later in Settings. |
* These messages come from the workflow's own checks in Validate and clean CSV. They were not triggered in the test run described here.
Limits
- One CSV per run. Comma-separated values, UTF-8 only.
- Each cell may hold up to 131,072 characters (Unicode code points). Spreadsheet apps may allow fewer.
- Header names are matched exactly, including case (after trimming spaces).
- Only spaces, tabs, line breaks and no-break spaces at the start and end of a cell are trimmed. Campaign IDs are never trimmed; an ID with a leading or trailing space is rejected.
- IDs are kept exactly as written in the CSV file the workflow writes. We did not test opening or importing that file in a spreadsheet app, so we make no claim about how such apps treat the IDs.
- Tested only as described at the top, and only with synthetic files.
How we tested
- An AI agent ran these tests, not a person. In this guide, "we" means Tracetern.
- n8n 2.42.7 and Node.js 24.21.0 were installed with npm from a pinned lock file, with n8n's default binary data mode. The test runs had no internet access.
- A Playwright script operated the editor in headless Chromium 141 on a new instance, signed in as its owner. It took Steps 2 to 5 through the editor's own menus, panels and buttons.
- The input and output folders were prepared ahead of time with the layout from Step 1. The commands in Step 1 were not run as shown.
- The written files were copied out of the test environment and checked by the contract checker. Each downloaded file was compared byte for byte with the written file of the same run.
- Screenshots, logs and the full run record are kept privately. They are not part of this article.
Sources
On 2026-10-13 we read the official n8n documentation from its source
repository (n8n-io/n8n-docs, commit aa9e7bd, 2026-10-12), because the
rendered pages could not be opened from the test environment. On the same
day, a separate AI agent checked the rendered pages listed below. Its
findings matched what we read in the repository.
Disclosure
Created with AI assistance. Tested with synthetic CSV files in the environment described above.