Report the first invalid JSON Lines record number
Also published in our primary archive.
To report the first invalid JSON Lines record number, iterate over the text stream with enumerate(..., start=1) and call json.loads() for each line. A whole-text json.loads() call only sees one JSON document; with this fixture, it reports Extra data after the first valid object instead of identifying the malformed second record. json.loads raises JSONDecodeError for invalid JSON, and that exception includes parsing-location attributes, but the stream loop supplies the record number directly.
The fixture deliberately contains valid, malformed, valid records. First, the code shows the unhelpful whole-file error. Next, it starts again with StringIO, accepts record 1, encounters {"id": } on record 2, prints that record number, and breaks. The final list demonstrates that record 3 was not decoded after the first error.
This is a fail-fast validator, not an error collector. Blank lines would also cause json.loads() to fail unless the code defines a separate blank-line policy. Likewise, “record number” here means physical input line number, so multi-line JSON values do not fit this simple JSON Lines contract. JSONDecodeError was added in Python 3.5; no newer API is required.
AI assistance disclosure: the example was AI-assisted and executed only against the shown in-memory fixture.
Example
import json
from io import StringIO
text = '{"id": 1}\n{"id": }\n{"id": 3}\n'
print(repr(text))
try:
json.loads(text)
except json.JSONDecodeError as error:
print(f"whole-file error: {error.msg}")
accepted = []
for record_number, line in enumerate(StringIO(text), start=1):
try:
accepted.append(json.loads(line))
except json.JSONDecodeError:
print(f"first invalid record: {record_number}")
break
assert accepted == [{"id": 1}]
print(accepted)
Expected output:
'{"id": 1}\n{"id": }\n{"id": 3}\n'
whole-file error: Extra data
first invalid record: 2
[{'id': 1}]