aillmjsonljson

Preparing Fine-Tuning Data: The JSONL Format Explained

·9 min read·AI & JSON

Why JSONL for fine-tuning

When you fine-tune a language model, the provider expects your training examples in JSONL (JSON Lines): one complete JSON object per line, with no enclosing array. It is the standard because each line is an independent record that can be streamed, validated, and counted without loading the whole file.

text
{"messages":[{"role":"system","content":"You are a support agent."},{"role":"user","content":"Where is my order?"},{"role":"assistant","content":"Let me check that for you."}]}
{"messages":[{"role":"user","content":"Reset my password"},{"role":"assistant","content":"I've sent a reset link to your email."}]}

Each line is a full training example; the newline is the only delimiter.

The chat message schema

Modern fine-tuning uses the same message format as the chat API. Each example is an object with a messages array, and the assistant message is the target — what you want the model to learn to produce. For structured-output fine-tuning, that target is itself JSON (escaped, since it lives inside a JSON string).

Building the file

Generate it programmatically and write one object per line:

python
import json

examples = [
    {"messages": [
        {"role": "user", "content": "Translate 'hello' to French"},
        {"role": "assistant", "content": "bonjour"},
    ]},
    # ...more...
]

with open("train.jsonl", "w", encoding="utf-8") as f:
    for ex in examples:
        f.write(json.dumps(ex, ensure_ascii=False) + "\n")

Use ensure_ascii=False so non-English text stays readable, and always end each line with a newline.

Validate before you upload

A single malformed line can fail an entire fine-tuning job. Validate that every line parses:

python
import json

with open("train.jsonl", encoding="utf-8") as f:
    for n, line in enumerate(f, 1):
        line = line.strip()
        if not line:
            continue
        try:
            obj = json.loads(line)
            assert "messages" in obj
        except Exception as e:
            print(f"Line {n} invalid: {e}")

Common JSONL mistakes

MistakeSymptomFix
Wrapping lines in a [ ... ] array"expected object, got array"Remove brackets; one object per line
Trailing comma after each lineParse errorJSONL has no commas between records
Pretty-printed (multi-line) objectsEach line fails to parseEach record must be on one line
Unescaped quotes in contentInvalid JSONLet the JSON library do the escaping
Blank lines / BOMSkipped or failed linesStrip blanks; save as UTF-8 without BOM

Quality beats quantity

A few hundred clean, on-task examples often outperform thousands of noisy ones. Keep formatting consistent — if the assistant target is JSON, make every example's target valid JSON with the same schema — and hold out a separate validation set to measure whether fine-tuning actually helped.

Frequently asked questions

JSON is one document (often an array). JSONL is many independent JSON objects, one per line, with no enclosing array and no commas between records.

No — each record must be a single line. Pretty-printing breaks the one-object-per-line rule. Format it only when inspecting a single record.

Iterate the array and write each element with the JSON library followed by a newline. A JSON-to-JSON-Lines converter does the same in the browser.

Try JSON Lines / NDJSON

Convert between JSON arrays and JSONL for your training data.