CSVFormat CSV Converter

CSV Patterns and Gotchas: Duplicate Headers, Ragged Rows, and Silent Pads

CSVFormat — CSV Formatter & Converter Guides · Updated 2026-10-12 · All guides

CSV fails by looking fine. A malformed JSON file throws; a malformed CSV file produces a slightly different dataset, and you find out from a report three weeks later. These are the six shapes that do it, with what this site's parser does to each — verified against the shipped code, not from memory.

1. Duplicate header keys

id,name,name
1,Ada,Lovelace

JSON is built by assigning obj[header] = value in header order, so the second name wins and the first value disappears: {"id":"1","name":"Lovelace"}. The row counter still says one row and the ragged-row check stays silent, because every row does have three columns. Nothing is wrong with the parse; the object model cannot hold two keys called name.

Deduplicate at write time (name,name_2) rather than hoping. If you cannot change the export, join on index instead of name — the SQL conversion is index-based, which is exactly why it survives this and JSON does not.

2. Ragged rows behave differently in each output

A row with fewer columns than the header:

  • JSON pads the missing keys with "". You get a complete object with invented empty values — the database stores an empty string where no data existed.
  • SQL emits exactly the values it found: INSERT INTO records (a, b, c) VALUES (1, 2);. Three columns, two values, rejected by the database.

Neither is "right". Padding loses information silently; truncating loses the row loudly. The warning line names the count either way, so read it before you copy the output.

3. A quote that does not open a quoted field

A quote is only treated as an opening quote when nothing precedes it in the field. Consequences, all real inputs:

InputParsed fieldWhy
a"ba"bmid-field quote is literal — correct, RFC-incompatible writers do this
"quoted""quoted"a leading space means the quote never opens, so the quote marks become data
"ok"tailoktailthe closing quote ends quoting and the tail is appended, not dropped

That second row is the expensive one: a space before a quote turns visible punctuation into part of your value, and downstream string comparisons stop matching.

4. Blank lines and trailing commas are not the same thing

A truly empty line is dropped — the last id in your file does not become "". But a line containing only the delimiter, ,,, survives as three empty fields, because a line of delimiters is a real record in RFC terms. Same asymmetry after a trailing comma: a,b, is three fields with an empty third, and a header that reads a,b, gives you a key called "".

5. The row count will not match your line count

This is not a bug, it is the format. A quoted field may contain a newline, so one record can span four lines. A 500-row export from a payment processor routinely arrives as 640 lines. Any pipeline that validates "line count equals expected rows" is wrong for every file containing an address with a newline in it, which is most address data.

Compare parsed row count to row count. The tool reports parsed rows and flags column-count mismatches, which is the same check a real parser does internally.

6. Numbers get rewritten on the way to SQL

The SQL output leaves a number unquoted only when it round-trips as a finite number. That guard is good and it is also where values change shape:

Input valueSQL outputChange
4242none
1e5100000notation rewritten
0x1016hex parsed as a number
42 42already trimmed by the parser, so the guard never sees the padding
0123456789123456789leading zero gone — phone, ZIP, FIPS, IBAN
90071992547409939007199254740992beyond 2^53, the last digit changes
1,234'1,234'stays quoted; Number() rejects it
''''empty string, not NULL

Quoted fields do not escape this: every cell is trimmed before conversion, so "01234" — explicitly quoted by the producer to say this is text — still arrives at the number test as bare 01234. CSV's only type signal is discarded on the way in. Keep any column of identifiers as text with a leading character, or run the JSON output and cast deliberately per column.

Related: best practices covers type inference and encoding; alternatives names the parsers that do not trim inside quotes.

Developer Sponsor / Partner
Copied to clipboard!