EMZETT.
Login

JSONL (JSON Lines)

In short: A file format in which every line on its own is a complete, valid JSON object – unlike a normal JSON file, which as a whole has to be a single object or array.

In more detail: Handy for logs and streams, because you can read and write line by line without parsing the whole file, and because new entries can simply be appended.

Our context: Claude Code stores session transcripts as JSONL (one line per event: user message, assistant answer, tool call, …) under ~/.claude/projects/<project>/<session-id>.jsonl. The Stop hook, which has since been removed again, accessed these files.

In Depth

Appending instead of rewriting

The advantage of JSONL over a single large JSON file shows especially with very long or growing data streams: a log writer can simply append each new line to the end of the file (fs.appendFile) without having to read, parse, modify and rewrite the entire existing file — with a normal JSON file (which as a whole has to be a valid array/object), that would be necessary, e.g. to add a new element to the end of an array. With a log file several gigabytes in size this difference isn’t just inconvenient but practically relevant: a complete rewrite for every single new entry would get slower and slower as the file grows, while appending stays constantly fast regardless of the existing file size.

Line-by-line processing without loading everything

Processing line by line (instead of loading everything into memory at once) is also simpler:

const readline = require("readline");
const stream = readline.createInterface({ input: fs.createReadStream("session.jsonl") });
for await (const line of stream) {
  const event = JSON.parse(line);
  // ... process line by line without keeping the whole file in memory
}

This is especially important for files that could become larger than the available memory — a JSON.parse() of an entire multi-gigabyte file would either take a very long time or abort straight away with an out-of-memory error, while line-by-line processing needs a constantly small amount of memory, regardless of the total size of the file.

Fault tolerance

A drawback of classic JSON files in this context: with JSONL, a single corrupted line (e.g. from an interrupted write, say because the process crashed in the middle of writing) can be skipped in isolation without making the whole file unreadable — with a classic JSON file, a single broken character somewhere in the middle would make parsing the entire file fail, even if 99% of the content were actually intact. This property makes JSONL particularly robust for use cases in which writes could be interrupted (e.g. a system crash or an abrupt end of the process).

Common areas of use

Besides session transcripts, JSONL is often used for training data in machine learning (each line one training example), for log aggregation (each line one log event, often with uniform fields such as timestamp and log level) and for data exchange between systems in which records are to be processed individually and independently of each other — e.g. when streaming database exports, where each line represents a single record.

See also: Claude Code Hooks, JSON