A file can look like valid JSON in chat while using the wrong record boundaries for its destination. The destination's own schema still matters: JSONL describes the framing, not the fields an application requires.
Show the source and the exact line boundary
These original teaching records describe two inspection notes. N1 contains a line break inside its note; N2 contains a literal backslash in a path. The following is the intended file content, displayed in a code block for this article:
{"id":"N1","note":"Door checked\nLatch pending"}
{"id":"N2","note":"Folder C:\\Reports"}
There are two physical record lines. The characters \n inside N1 encode a line break in the decoded note. They do not start another record in the file. Likewise, \\ encodes one backslash in the decoded path. Do not replace either escape with an unescaped character while copying the content.
The JSON Lines format description, checked October 24, 2026, requires UTF-8 and a valid JSON value on each line. A blank line is not a valid record. The format allows values other than objects, but this example's importer contract requires objects with id and note strings. A final newline is recommended and does not itself create an extra blank record.
Validate the framing separately from the fields
Save this Node.js checker as check-jsonl.mjs. It reads a supplied file without modifying it. It accepts LF or CRLF separators and permits a single final separator. It reports failures by physical line number:
import fs from "node:fs";
const text = fs.readFileSync(process.argv[2], "utf8");
const lines = text.split(/\r?\n/);
if (lines.at(-1) === "") lines.pop();
const seen = new Set();
let failed = false;
for (let i = 0; i < lines.length; i++) {
try {
const record = JSON.parse(lines[i]);
if (!record || Array.isArray(record) ||
typeof record !== "object" ||
typeof record.id !== "string" ||
typeof record.note !== "string") {
throw new Error("expected object with string id and note");
}
if (seen.has(record.id)) throw new Error("duplicate id");
seen.add(record.id);
console.log(`line ${i + 1}: OK ${record.id}`);
} catch (error) {
failed = true;
console.error(`line ${i + 1}: ${error.message}`);
}
}
if (!lines.length) failed = true;
process.exitCode = failed ? 1 : 0;
Run node check-jsonl.mjs notes.jsonl. For the two records above, expect OK N1 and OK N2 on their corresponding lines and exit status zero. Add a blank line between them and the checker should report that line as invalid. An indented JSON array also fails this object-per-line contract.
This small checker does not reject extra properties, prove UTF-8 decoding was lossless, or compare every value with its source. Use the actual importer's validator for its full contract. The separate JSON Schema guide addresses field-level rules.
Request the conversion without silent cleanup
Convert these supplied records to JSONL for [DESTINATION].
Required object fields and types: [PASTE EXACT CONTRACT]
Source records and stable IDs: [PASTE PERMITTED DATA]
Keep one complete object per physical line. Preserve record order,
IDs and original values. Encode embedded line breaks and backslashes
using JSON escapes. Do not wrap records in an array or add comments.
Return the file content separately from any explanation.
List missing fields or ambiguous source values separately; do not
silently drop, repair, merge or invent records. Do not import anything.
OpenAI's prompting guidance supports specifying the task and output boundaries. The examples here are authored fixtures, not a live ChatGPT result or an OpenAI training-file contract.
Before importing, compare the complete expected ID list and decoded values with the source. A syntactically valid line can still contain the wrong note. Keep the original source and any rejected-line receipt so a correction does not erase which record failed.