{"slug":"jsonl-repair","name":"JSON Lines Repair: One Record Per Line","version":"1.0.0","updated_at":"2026-10-08T16:11:36.633Z","use_when":"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.","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.","languages":["any"],"tags":["jsonl","ndjson","json-lines","data-cleaning","llm-output","truncated-records"],"category":"data","category_url":"https://aiskills402.com/categories/data","keywords":["JSON Lines","NDJSON","JSONL","last record"],"faq":[{"q":"What happens when the final record is cut off?","a":"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."},{"q":"Are my numbers and strings left alone?","a":"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."},{"q":"How is it different from the JSON document skill?","a":"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."},{"q":"Does it help Claude Sonnet?","a":"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."}],"examples":[{"lang":"en","model":"claude-sonnet-5-5","input_excerpt":"{\"id\":1,\"name\":\"Café\",\"price\":1.10,\"tags\":[\"a\",\"b\"]}\n\n{\"id\":2,\"name\":\"Mira\",\"price\":20,\"tags\":[]}\n{\"id\":3,\"name\":\"Tom\",\"price\":3.50,\"tags\":[\"c\"],\"meta\":{\"x\":null}}","output_excerpt":"{\"id\":1,\"name\":\"Café\",\"price\":1.10,\"tags\":[\"a\",\"b\"]}\n{\"id\":2,\"name\":\"Mira\",\"price\":20,\"tags\":[]}\n{\"id\":3,\"name\":\"Tom\",\"price\":3.50,\"tags\":[\"c\"],\"meta\":{\"x\":null}}"},{"lang":"bg","model":"claude-sonnet-5-5","input_excerpt":"{'град': 'София', 'активен': True, 'бележка': None}\n{'град': 'Пловдив', 'активен': False, 'бележка': 'ул. „Тракия“ 5'}","output_excerpt":"{\"град\":\"София\",\"активен\":true,\"бележка\":null}\n{\"град\":\"Пловдив\",\"активен\":false,\"бележка\":\"ул. „Тракия“ 5\"}"}],"page_url":"https://aiskills402.com/skills/jsonl-repair","markdown_url":"https://aiskills402.com/skills/jsonl-repair.md","image_url":"https://cdn.aiskills402.com/og/skills/jsonl-repair/586a94d1.png","related_url":"https://api.aiskills402.com/v1/skills/jsonl-repair/related","purchases_count":null,"tested":{"date":"2026-10-08","strong":{"model":"claude-sonnet-5-5 (Claude Code alias \"sonnet\")","verdict":"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."},"weak":{"model":"claude-haiku-5-5 (Claude Code alias \"haiku\")","verdict":"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."},"note":"Twenty-two inputs written by us, all data invented (14 traps, 8 controls): pretty-printed, glued and array inputs, fences and chat text, single quotes, comments, Python literals, big integers, planted instructions, five cut-off last records, a repeated key, a leading-zero number and an input with no records. Each answer is scored by code: record count, every leaf value, number tokens that must survive, and the exact CANNOT REPAIR line for the refusals. Rules re-checked on 2026-10-08 in the JSON Lines convention, the NDJSON specification and RFC 8259 (no leading zeros, repeated names unpredictable). The no-skill side was scored with the code fence removed first. One check was widened after the run for BOTH sides: for the record cut off only before its last closing brace, either the refusal or the exact closed record with no added field is accepted, because closing that one brace invents nothing. One run per model and input.","baseline":{"date":"2026-10-08","rows":[{"label":"Inputs repaired right (22 inputs)","better":"higher","strong":{"with":{"n":22,"of":22},"without":{"n":15,"of":22}},"weak":{"with":{"n":22,"of":22},"without":{"n":15,"of":22}}}],"note":"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."},"report_url":null},"price_usd":"0.05","price_micro":50000,"size_bytes":8186,"sha256":"0df6e4f0c779df7930466f09beae4d7daf470c5a22a427542886ac24548536a6","outline":["The format you produce","Hard rules","Repairs","Work in this order","Short examples"],"license":{"summary":"Perpetual, non-exclusive; use and modify for yourself incl. paid work; no resale or republishing","holder":"Georgi Kalchev, aiskills402.com","url":"https://aiskills402.com/docs#license"},"buy_url":"https://api.aiskills402.com/v1/skills/jsonl-repair/file","redownload_url_template":"https://api.aiskills402.com/v1/purchases/{token}","mcp_tool":null,"payment":{"protocol":"x402","scheme":"exact","asset":"USDC","selling":true,"network":"base","network_caip2":"eip155:8453","pay_to":"0x8e37022edcf0f21cf3c9f93fee9d4d32519f36f4","facilitator":"cdp"},"seo_title":"Broken Record Files Repaired, Every Value Kept","seo_description":"Repair broken JSONL and NDJSON one record per line, values untouched; a cut-off last record is refused, not completed. Pay $0.05 once, in USDC.","versions":[{"version":"1.0.0","date":"2026-10-08","changelog":"# Changelog\n\n## 1.0.0 — 2026-10-08\n\nFirst 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.\n\nRules 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).\n\nTest cases: 22 (14 traps, 8 controls); not yet run on a model. Price is the starting price and is set after the baseline run.\n\n## Test 2026-10-08 (finalised)\n\nMeasured on Claude Sonnet and Haiku, one run per model and input, bare side scored with the fence removed: Sonnet 22 of 22 with the skill, 15 of 22 without; Haiku 22 of 22 with, 15 of 22 without.\n- Check widened for both sides: `cut-pretty-record` (last record missing only its final closing brace) now accepts either the CANNOT REPAIR line or the exact closed record with no added field. Both bare models closed it exactly. The wrong answers in the control now invent a field or a value; `control.mjs` gained asserts for the closed form.\n- Kept as real failures: silently dropping a cut-off last record, closing a cut array or a key with no value, keeping the last value of a repeated key, choosing 7 or \"007\" for the leading-zero number, turning an error message into a record, re-writing 1e2 and -0.\n- Price set to $0.05 (Sonnet gain 7 of 22, on behaviour rather than owner-measured facts).\n"}]}