Notes · Sep 6, 2026

Fine-tuning JSONL: the four ways the upload refuses the file

The upload says the file is not valid JSONL and stops there. The file is fine everywhere but one line, and the message does not say which.

Validate JSON LinesFive records, one that will not parse: the verdict counts it, the row names it, and Fix all rewrites that line alone. · 8 s

What the upload checks first

Before any rule about roles or token counts, a fine-tuning endpoint checks the one thing JSON Lines promises: every line is one complete JSON value, and there is nothing else. A file can pass every field rule and fail that, and the refusal names the format, not the line. The four ways it happens are the same four every time, and none of them is visible in an editor.

billing-train.jsonl
{"messages": [{"role": "system", "content": "You answer billing questions."}, {"role": "user", "content": "Why was I charged twice?"}, {"role": "assistant", "content": "One charge is a pending authorisation; it drops off within 7 days."}]}
{"messages": [{"role": "user", "content": "Can I get an invoice?"}, {"role": "assistant", "content": "Yes — Settings → Billing → Invoices."}],}
{
  "messages": [
    {"role": "user", "content": "Do you support SEPA?"},
    {"role": "assistant", "content": "Yes, in the EU and the UK."}
  ]
}
…

The four ways

Each one comes from a tool that meant well.

  • A byte-order mark before line 1 — an editor or a spreadsheet saved the file as UTF-8 with a signature, and a strict reader refuses the first line
  • A record pretty-printed over several lines — an editor’s Format command or a script written with indent=2, so one record is six broken lines to a reader that counts lines
  • A trailing comma, or a line printed by Python with its own quotes — a script that wrote records the way it printed them
  • A JSON array in a file renamed .jsonl — the whole file is one value, not one per line

What the sample says on the page

The file above answers “JSON Lines · 5 records · 3 won’t parse · a BOM before line 1 · 4 fixable”. Line 1: a byte-order mark before line 1. Line 2: extra comma at the end. Line 3: one record written across 6 lines. Line 9: single quotes → double. Fix all: 4 lines changed · every one of the 5 records parses, and the records that were fine are not touched.

The array is the other file. Saved as one JSON value, it answers “JSON · one array of 2 records, not JSON Lines · 1 fixable” — and the fix writes one record per line, the same text Convert → NDJSON writes: 2 lines · every one parses.

What the page does not check

The field rules — a messages array, a role on each turn, an assistant turn last, the token limit per example — belong to the service, and the page does not pretend to know them. What it does is the half the service never explains: which line, what is wrong with it, and the fix by name. Paste the file; the broken lines are named, and the file that comes back is the one the upload takes.

Try it

Try it on the file that broke your afternoon.

free while in alpha · no signup