I ran into this with a log export last year. A few hundred thousand lines, each one a perfect little JSON object, and my script died on the first character of line two. The file looked fine in every editor. The error message was the classic "Unexpected token" pointing at a spot that contained nothing but a newline. That contradiction is the signature of this problem: you are feeding a multi-record format into a single-document parser.
What NDJSON actually is
NDJSON, also called JSON Lines (files usually end in .jsonl or .ndjson), is one complete JSON value per line, separated by newline characters. No wrapping array, no commas between records, no outer object. It is the format of log streams, bulk API exports, and training datasets, because you can append a record without rewriting the file and you can process it without loading the whole thing into memory.
{"event":"signup","user":"a41","ts":1759257600}
{"event":"purchase","user":"a41","ts":1759258200}
This is not valid as a single JSON document. There is no grammar rule that allows two objects sitting next to each other. The parser reads the first object, hits the newline expecting the document to be over, finds more text, and gives up.
The four mistakes that break line-based parsing
1. Parsing the whole file at once. This is the big one, described above. The fix is structural: split first, parse second.
2. Pretty-printed records. Some exporters write each record across multiple indented lines. That is not NDJSON anymore, and no line splitter can handle it. One record must equal one line, always.
3. Trailing blank lines. Most files end with a newline after the last record, which leaves an empty final "line" when you split. An empty string is not valid JSON, so filter blank lines before parsing or you will get a phantom error at the very end.
4. Carriage returns and BOMs. Files from Windows tools use \r\n line endings, and some start with a byte-order mark. Strip the BOM and trim each line before parsing.
The fix, in JavaScript
This is the whole pattern. It handles every issue above and tells you exactly which line failed:
const records = [];
for (const [i, raw] of text.replace(/^\uFEFF/, "").split("\n").entries()) {
const line = raw.trim();
if (!line) continue; // skip blank lines
try {
records.push(JSON.parse(line));
} catch (e) {
console.error(`Line ${i + 1} is broken: ${line.slice(0, 80)}`);
}
}
In Python it is the same idea: [json.loads(l) for l in f if l.strip()]. The line number in the error is the part most people skip, and it is the part that saves you. When line 48,211 of 50,000 is corrupt, you want to know it is line 48,211.
My honest opinion: if you are writing an exporter, validate each line as you write it. A corrupt record buried in the middle of a 2 GB file is a miserable thing to discover downstream.
Paste one broken line and get the fixed version back, with a plain-English explanation of what was wrong.
Fix My JSON NowFrequently asked questions
What is the difference between JSON and NDJSON?
JSON is a single document: one object, array, or value. NDJSON is a stream format: many JSON documents, one per line, with no wrapper. They are parsed differently, which is why a whole NDJSON file fails JSON.parse.
Why does JSON.parse fail on my .jsonl file?
Because JSON.parse expects one complete document and your file contains many, separated by newlines. Split the text on newlines, skip blank lines, and parse each line individually.
Does NDJSON need a trailing newline at the end of the file?
It is conventional but not required. Most parsers accept a final line with no trailing newline. If yours does not, that is a parser bug, not a format requirement.
Can NDJSON records contain nested objects and arrays?
Yes. Nesting is fine. The only rule is that each record occupies exactly one line, so nested values cannot contain literal newlines. Newlines inside string values must be escaped as \n.
Can I convert NDJSON to regular JSON?
Yes: parse each line, collect the results into an array, and serialize the array. That gives you one valid JSON document. For large files, consider keeping it as NDJSON and streaming instead of converting.
Get one practical dev-tools guide a week: subscribe to the newsletter for new JSON guides and parser explainers.
Related reading: Why Is My JSON Invalid? The 7 Syntax Errors That Break JSON.parse · JSON with Comments (JSONC): What It Is and How to Handle It