Malformed data and error handling

csv · Expert · 12 min

Real-world files are messy; a robust importer reports exactly which row failed and why instead of crashing or silently corrupting data.

The reader is lenient by default: an unbalanced quote may silently swallow the rest of the file into one field, and a stray quote in the middle of a field is kept as text. Make problems visible:

  • strict=True raises csv.Error on bad CSV input, such as a character after a closing quote.
  • A file ending inside a quoted field raises csv.Error: unexpected end of data when strict=True is on.
  • reader.line_num gives the physical line where parsing stopped, so error messages can point at it.
  • csv.field_size_limit() (default 131072 characters) guards against a runaway field; exceeding it raises csv.Error: field larger than field limit. Raise it deliberately with csv.field_size_limit(n) if you have big legitimate fields.

Ragged rows (wrong number of fields) aren't CSV syntax errors, so validate them yourself: compare len(row) with the header, or use DictReader's restkey (extra values) and restval (missing values) to detect them.

A robust importer separates concerns: parse with csv (syntax), validate row by row (shape and types), collect errors with line numbers instead of stopping at the first, and decide on a policy: reject the file, skip bad rows, or quarantine them to a separate file.

Example

import csv, io

data = 'id,name,qty\n1,Ada,3\n2,"Bob"x,5\n3,Cy\n4,Di,4,EXTRA\n'
errors, good = [], []
reader = csv.DictReader(io.StringIO(data), strict=True, restkey="_extra")
while True:
    try:
        row = next(reader)
    except StopIteration:
        break
    except csv.Error as e:
        errors.append((reader.line_num, f"syntax: {e}"))
        continue
    if row.get("_extra") or None in row.values():
        errors.append((reader.line_num, "wrong number of fields"))
    else:
        good.append(row)

print([r["id"] for r in good])  # ['1']
for line, msg in errors:
    print(line, msg)
# 2 syntax: ',' expected after '"'   (line_num = last line fully read)
# 4 wrong number of fields
# 5 wrong number of fields

Remember

  • strict=True turns malformed quoting into csv.Error
  • reader.line_num locates problems; field_size_limit caps huge fields
  • Check field counts yourself; restkey/restval reveal ragged rows

Watch out for

  • Running a lenient reader over a file with an unbalanced quote, merging thousands of rows into one field.

Check yourself

  1. Why can a single unbalanced quote be so damaging in non-strict mode?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/csv.html#csv.field_size_limit