csv.reader Ignores lineterminator= (tested)

csv.writer(f, lineterminator="|||") happily writes rows separated by |||. Pass that exact same lineterminator="|||" to csv.reader() expecting it to read the file back correctly, and it silently fails — no error, just one mangled row.

1. Writing with a custom lineterminator works as expected

import csv, io

f = io.StringIO()
w = csv.writer(f, lineterminator="|||")
w.writerow(["a", "b"])
w.writerow(["1", "2"])
f.getvalue()
'a,b|||1,2|||'

Measured: exactly as documented, csv.writer uses whatever string you give lineterminator to end each row.

2. Reading it back with the matching lineterminator fails silently

f.seek(0)
list(csv.reader(f, lineterminator="|||"))
[['a', 'b|||1', '2|||']]

Measured: setting the identical lineterminator="|||" on csv.reader produces no error, but also does nothing useful — the two rows got merged into one broken row, with ||| treated as ordinary field content. csv.reader accepts the lineterminator keyword without complaint, which looks like it should work symmetrically with the writer, but it silently has no effect on parsing at all.

3. Why: the reader is hard-coded to split on \r or \n only

Per the Python csv docs, this is explicitly documented, if easy to miss: "The reader is hard-coded to recognise either \r or \n as end-of-line, and ignores lineterminator. This behavior may change in the future." Our test confirms that's still true in 3.13.5 — the keyword argument is accepted (no TypeError), stored on the dialect object, and then simply never consulted while parsing rows.

4. What to actually do with a non-standard line terminator

Don't rely on csv.reader(..., lineterminator=...) to parse a file that uses a non-\r\n/\n row separator — it will not do what the symmetrical-looking writer argument suggests. If you control the format, keep the default \r\n on the write side (or just don't set lineterminator at all) so the file is readable by a normal csv.reader. If you're stuck reading a file that genuinely uses a custom separator, split the raw text on that separator yourself first (e.g. text.split("|||")) and feed the resulting list of row-strings to csv.reader, rather than trying to make the reader understand the separator directly.

Frequently asked questions

Does csv.reader() support a custom lineterminator the way csv.writer() does?

No — we measured that csv.reader() accepts the lineterminator= keyword with no error, but it has zero effect on parsing. The reader always splits rows on a literal \r or \n, exactly as the Python docs state explicitly.

Why didn't csv.reader() raise an error for an unsupported lineterminator?

Because the argument is accepted and stored on the Dialect object just like for the writer — it's only ever read by the writer's row-ending logic, never consulted by the reader's parsing logic, so there's nothing for it to reject.

How do I read a CSV-like file that uses a non-standard row separator?

We'd split the raw text on that separator first with Python's own string .split(), then pass the resulting list of row-strings to csv.reader(), since the reader itself cannot be configured to recognize anything besides \r or \n as end-of-line.

Is this lineterminator behavior documented?

Yes — the Python csv docs state it directly: the reader is hard-coded to recognise \r or \n as end-of-line and ignores lineterminator. We confirmed this is still accurate behavior in Python 3.13.5.

What's the safest lineterminator to use when writing CSV for later reading?

We'd avoid setting lineterminator= at all on the writer side, or leave it at its default \r\n — that guarantees any standard csv.reader (which only understands \r/\n) can read the file back correctly.

Written with AI assistance. Measured on 2026-10-03 with Python 3.13.5's built-in csv module: wrote CSV data with a custom lineterminator via csv.writer, then read the identical text back with csv.reader configured with the same lineterminator, recording the exact (broken) result. PlainHeaders is our extension; Python's csv module is built in.