Data & Analysis

JSON Lines Repair: One Record Per Line

Repairs broken JSON Lines (NDJSON) so that every line is exactly one JSON record and no value changes. Re-joins records that were pretty-printed over several lines, splits records that were glued together on one line, unwraps a JSON array into lines, drops blank lines, code fences and chat text between records, and fixes trailing commas, single quotes, unquoted keys, comments and Python literals inside each record. Numbers are copied as written, never re-serialised. A last record that was cut off, a record with a repeated key, and a leading-zero number that could be a string or a number are refused with a fixed CANNOT REPAIR line instead of being completed or guessed. Use when a log export, a training dataset, a model answer or an API stream was meant to be JSON Lines and a line-by-line parser rejects it, or when asked to fix, clean up or validate NDJSON or JSONL.

JSON Lines Repair: One Record Per Line is a tested SKILL.md that repairs broken JSON Lines (NDJSON) so that every line is exactly one JSON record and no value changes; an agent buys it once for $0.05 over x402.

Tested 2026-10-08No code, no hidden instructionsv1.0.0 · 8.0 KB · perpetual license

Not for

Checking whether the records are true or fit a schema; guessing values that were cut off; converting between CSV and JSON; one big JSON document that should stay one document. When the last record is cut off, a record repeats a key, or a leading-zero number could be text or a number, it answers with a CANNOT REPAIR line instead of choosing.

Tested, honestly

Tested 2026-10-08 with a strong and a weak model.

With and without the skill

Results with and without the skill, for Sonnet and Haiku
SonnetHaiku
withwithoutwithwithout
Inputs repaired right (22 inputs)22/2215/2222/2215/22

Same request on both sides. Read by hand, Sonnet without the skill already did every plain repair, copied number tokens, quoted a leading-zero ZIP code whose neighbours were text, and closed the record cut off only before its final brace without adding a field. It missed the cases where the data decides: it dropped a cut-off last record without saying so (twice), invented the end of two others (a closed array, a null), kept the last value of a repeated key, turned 007 into 7, and turned 'Error 503' into a record. Haiku missed the same cases and two more: it finished the cut name Ol as a record and re-wrote 1e2 as 100 and -0 as 0. It quoted 007 as text, one of two readings, picked silently.

Same cases and the same checks with and without the skill. The cases are ours, written around what the skill is for; with a handful of cases, a difference of one or two is within noise.

SonnetStrong model, claude-sonnet-5-5
Right on all 22 inputs, read by hand: it re-joined pretty-printed records, split glued ones, unwrapped an array, fixed single quotes and Python literals, and copied 1.10, 1e2, -0 and a twenty-digit integer as written. It refused with a CANNOT REPAIR line for the four cut-off last records, the cut inside a glued line, the repeated key, the leading-zero 007 whose neighbours are numbers and the input with no records, and it kept the planted orders inside strings and between records as plain data.
HaikuWeak model, claude-haiku-5-5
Right on all 22 inputs, read by hand, with the same refusals as Sonnet: the cut-off records, the repeated key, the ambiguous 007 and the error message with no records. It copied the number tokens, left the planted instructions alone and returned bare records with no fence.

Full test summary

Example

Our own test text, before and after the skill ran. Excerpts only.

English · claude-sonnet-5-5

Before

{"id":1,"name":"Café","price":1.10,"tags":["a","b"]} {"id":2,"name":"Mira","price":20,"tags":[]} {"id":3,"name":"Tom","price":3.50,"tags":["c"],"meta":{"x":null}}

After

{"id":1,"name":"Café","price":1.10,"tags":["a","b"]} {"id":2,"name":"Mira","price":20,"tags":[]} {"id":3,"name":"Tom","price":3.50,"tags":["c"],"meta":{"x":null}}

Bulgarian · claude-sonnet-5-5

Before

{'град': 'София', 'активен': True, 'бележка': None} {'град': 'Пловдив', 'активен': False, 'бележка': 'ул. „Тракия“ 5'}

After

{"град":"София","активен":true,"бележка":null} {"град":"Пловдив","активен":false,"бележка":"ул. „Тракия“ 5"}

What is in the file

  • The format you produce
  • Hard rules
  • Repairs
  • Work in this order
  • Short examples

Languages

Any language. Tried in: English, Bulgarian.

License

Perpetual, non-exclusive; use and modify for yourself incl. paid work; no resale or republishing. Holder: Georgi Kalchev, aiskills402.com. Full terms.

Versions

Current version 1.0.0, updated 2026-10-08. Whoever bought an earlier version gets new ones free through the same re-download token.

  1. v1.0.0 · 2026-10-08

    First draft: repairs broken JSON Lines (NDJSON) record by record without changing a value. Re-joins pretty-printed records, splits glued records, unwraps an array, drops blank lines, fences and chat text, fixes trailing commas, single quotes, unquoted keys, comments and Python literals. Numbers are copied as written. A cut-off last record, a repeated key and an undecidable leading-zero number get a fixed CANNOT REPAIR line.

    Rules checked on 2026-10-08 by read-only fetch of the JSON Lines convention (UTF-8, no byte order mark, one value per line, line feed terminator, blank lines are not values), the NDJSON specification (records never contain raw line breaks, parsers may skip empty lines, UTF-8) and RFC 8259 (number grammar without leading zeros, section 4: names within an object should be unique and behaviour on repeats is unpredictable, section 6: only integers up to 2 to the 53rd minus one interoperate).

    Test cases: 22 (14 traps, 8 controls); not yet run on a model. Price is the starting price and is set after the baseline run.

FAQ

What happens when the final record is cut off?

You get one line that begins with CANNOT REPAIR: followed by the input line where the record begins. Nothing is closed, dropped or guessed, and the earlier records are not handed back as if the file were whole, so a program that reads the first line knows a person has to look. In a training set or a log, one invented row does more harm than a stopped job, because nobody sees it.

Are my numbers and strings left alone?

Yes. Any number that is already valid JSON is copied exactly as written, so 1.10 stays 1.10, 1e2 stays 1e2 and an integer of twenty digits keeps every digit. A string such as "1,250" stays a string. A number with a leading zero is quoted only when the other records show that its field holds text, and otherwise the skill refuses instead of choosing between 7 and the text 007.

How is it different from the JSON document skill?

The sibling skill repairs a single document and turns a value that was cut off into null. This one works per record, keeps the file a list of lines, and refuses a cut-off final record instead of nulling it. It also refuses a repeated key inside a record, because parsers disagree on which value wins, and it treats orders hidden in the data as plain data. Pick it when each line is a row that something else will read later, and pick the sibling when you hold one nested document.

Does it help Claude Sonnet?

Without the skill Sonnet got 15 of 22 inputs right, with it 22 of 22. It already managed every plain repair and copied number tokens. It missed the cases where the data decides: a truncated final line (dropped silently or finished with invented values), a repeated key, the leading-zero 007, and an error message that it turned into a record. Haiku went from 15 to 22 the same way.

Share

Read this page as Markdown: /skills/jsonl-repair.md.

  • JSON Repair: Fix It, Keep Every Value

    Data & Analysis

    SKILL.md · v1.0.1 · 6.9 KB

    Repairs broken JSON so a program can parse it, without changing any value. Fixes trailing and missing commas, comments, single quotes, unquoted keys, Python and JavaScript literals, smart quotes used as delimiters, raw line breaks and stray quotes inside strings, a code fence or chat text around the JSON, and output cut off in the middle. A value that was cut off becomes null instead of a guess, numbers JSON cannot hold as written are kept as strings, and when the structure can be read two ways the answer says so instead of picking one. Use when a tool, an API or another model returned JSON that does not parse, or when asked to fix, clean up, validate or close invalid or truncated JSON.

    $0.05once

    • x402
    • USDC
    • Base
    Get skill

    Tested with Sonnet and Haiku, 7 Oct 2026

  • CSV Repair: Fix the Form, Keep Every Cell

    Data & Analysis

    SKILL.md · v1.0.0 · 8.3 KB

    Repairs broken or messy CSV so a standard parser reads it, without changing the text of a single cell, and answers with the CSV only, no code fence. Fixes quotes that do not close, a quote inside an unquoted cell, cells that hold the delimiter or a line break, rows shorter than the header, a Markdown table, and chat text or a fence around the data. Numbers such as 1,250, 007 and 1.10, dates, spaces inside cells and values that look like formulas stay exactly as written. Semicolon and tab files keep their delimiter. A row longer than the header, a row that could be read two ways, an unbalanced quote that no reading closes, an HTML page and prose with no table get a fixed one-line CANNOT REPAIR answer instead of a guess that moves data between columns. Use when an export, an API or another model returned CSV that does not parse, or when asked to fix, clean up, close or convert a table to valid CSV.

    $0.05once

    • x402
    • USDC
    • Base
    Get skill

    Tested with Sonnet and Haiku, 8 Oct 2026