csv DictWriter Forgetting writeheader() (tested)

csv.DictWriter needs fieldnames= to know the column order, but it does not write a header row for you — you have to call .writeheader() yourself. We measured what happens if you forget: no exception, no warning, just a file missing its header, and a very misleading result if you read it back.

1. Forgetting writeheader() produces a file with no header row

import csv, io

buf = io.StringIO()
w = csv.DictWriter(buf, fieldnames=["name", "age"])
w.writerow({"name": "Alice", "age": 30})
w.writerow({"name": "Bob", "age": 25})

buf.getvalue()
'Alice,30\r\nBob,25\r\n'

Measured: both writerow() calls succeed with no error, no warning, and no indication anything is missing. fieldnames is only used internally to pick out dict values in the right order — it is never written to the output unless .writeheader() is called separately.

2. Reading it back silently corrupts your first real row into a header

buf.seek(0)
r = csv.DictReader(buf)
for row in r:
    print(row)
{'Alice': 'Bob', '30': '25'}

Measured: csv.DictReader always treats the first line of the file as the header, with no way to know one wasn't written. "Alice" and "30" silently become the dict keys, and the only data row left is Bob's — Alice's entire record is gone, replaced by malformed keys that look like real column names if you don't notice the output values are wrong. No exception is raised anywhere in this whole round trip.

3. Why this is easy to miss

Per the csv docs, writeheader() is explicitly a separate method you must call — DictWriter.__init__ never calls it automatically, unlike some other languages' CSV libraries that write a header by default. Because writerow() and writerows() both succeed normally without it, small scripts that only check "did an exception get raised" or "does the file have the right number of rows" pass every check while silently producing headerless output.

4. Fix: always call writeheader() before the first writerow()

buf2 = io.StringIO()
w2 = csv.DictWriter(buf2, fieldnames=["name", "age"])
w2.writeheader()
w2.writerow({"name": "Alice", "age": 30})
w2.writerow({"name": "Bob", "age": 25})

buf2.getvalue()
'name,age\r\nAlice,30\r\nBob,25\r\n'

Measured: calling .writeheader() once, immediately after constructing the DictWriter and before any writerow() calls, writes fieldnames as the first line — confirmed the file round-trips correctly through DictReader afterward with every value under its real column name. There's no flag or constructor argument to make this automatic; it's a required manual step every time.

Frequently asked questions

Does csv.DictWriter write a header row automatically?

No — we measured it writing zero header bytes unless .writeheader() is called explicitly. fieldnames= is only used to order each row's values, not written to the file by itself.

What happens if I forget csv.DictWriter.writeheader() and read the file back?

We measured csv.DictReader silently treating the first data row's values as the header (becoming the dict keys), losing that row's data entirely — with no exception or warning anywhere in the process.

Does csv.DictWriter raise an error if I never call writeheader()?

No — we confirmed writerow() and writerows() both succeed normally with no header written. There's no validation that a header exists; the file is simply missing one.

How do I fix a CSV file that's missing its header because writeheader() was forgotten?

Based on the csv docs, you'd need to know the original fieldnames and prepend them as the first line yourself — csv.DictWriter has no way to add a header after rows have already been written.

When should I call DictWriter.writeheader()?

We confirmed calling it once, right after creating the DictWriter and before any writerow() calls, is what produces a correctly-ordered header matching fieldnames=.

Written with AI assistance. Measured on 2026-10-03 with Python 3.13.5's built-in csv module: wrote two rows via csv.DictWriter without calling writeheader() first, then read the resulting file back with csv.DictReader. PlainHeaders is our extension; Python's csv module is built in.