{"record":{"id":"aa18f9820d29df07","repo":"Hmbown/CodeWhale","slug":"invalid-json-on-nonempty-line-i-1-import-cancelled-no-rows","errorCode":null,"errorMessage":"Invalid JSON on nonempty line ${i + 1}. Import cancelled; no rows were skipped.","messagePattern":"Invalid JSON on nonempty line (.+?)\\. Import cancelled; no rows were skipped\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/src/core/ingest.ts","lineNumber":231,"sourceCode":"  }\n  return { events, origins: new Map([...bases].map(([k, v]) => [k, v.toString()])), warnings };\n}\n\n/** Parse strictly: malformed lines or duplicate identities never disappear silently. */\nexport function importTrace(text: string, filename = 'Imported trace', options: ImportOptions = {}): Trace[] {\n  const mode = options.privacy ?? 'redact', maxEvents = options.maxEvents ?? 250_000;\n  if (!['redact', 'metadata', 'retain'].includes(mode)) throw new Error('Unknown privacy mode.');\n  const maxTraces = options.maxTraces ?? 8;\n  if (!Number.isInteger(maxEvents) || maxEvents < 1 || maxEvents > 250_000) throw new Error('maxEvents must be in [1, 250000].');\n  if (!Number.isInteger(maxTraces) || maxTraces < 1 || maxTraces > 64) throw new Error('maxTraces must be in [1, 64].');\n  if (new TextEncoder().encode(text).length > (options.maxBytes ?? 64 * 1024 * 1024)) throw new Error('File exceeds the 64 MiB MVP import limit. Split the export by trace.');\n  const trimmed = text.replace(/^\\uFEFF/, '').trim();\n  if (!trimmed) throw new Error('The trace file is empty.');\n  let document: unknown;\n  try { document = JSON.parse(trimmed); }\n  catch {\n    document = trimmed.split(/\\r?\\n/).filter(l => l.trim()).map((line, i) => {\n      try { return JSON.parse(line); } catch { throw new Error(`Invalid JSON on nonempty line ${i + 1}. Import cancelled; no rows were skipped.`); }\n    });\n  }\n  // Transform before both normalization and raw retention, so raw cannot bypass redaction.\n  const safe = mode === 'retain' ? (options.redactor ? options.redactor(document, '') : document) : redact(document, '', options.redactor);\n  const root = obj(safe);\n  if(root.format === 'whalesong.evidence/v1') return [evidenceToTrace(validateBundle(root, Math.min(maxEvents, 100_000)))];\n  if (isCodewhaleSession(safe)) {\n    const trace = fromCodewhaleSession(safe, filename, maxEvents);\n    trace.privacy = mode;\n    trace.events = trace.events.map(e => privacyEvent(e, mode));\n    return [trace];\n  }\n  const records = Array.isArray(safe) ? safe : Array.isArray(root.events) ? root.events : null;\n  if (isCodewhaleRuntimeDocument(records ?? [safe])) {\n    const trace = fromCodewhaleRuntime(records ?? [safe], filename, maxEvents);\n    trace.privacy = mode;\n    trace.events = trace.events.map(e => privacyEvent(e, mode));\n    return [trace];","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/src/core/ingest.ts#L213-L249","documentation":"When the whole input is not a single valid JSON document, importTrace falls back to line-delimited JSON (JSONL). Each nonempty line must parse; the first line that fails aborts the entire import with this error naming the 1-based line number, guaranteeing no rows are skipped silently.","triggerScenarios":"importTrace with mixed/truncated JSONL (a partially written last line), plain text or CSV passed as JSON, pretty-printed JSON whose lines are individually invalid (it fails JSON.parse then each line fails), or concatenated JSON objects without newline separation.","commonSituations":"File truncated by a crash or incomplete download; wrong file selected (a log or CSV instead of JSONL); editing a JSONL file by hand; export written without a trailing flush.","solutions":["Open the file at the reported line number and fix or remove the malformed line.","Verify the file is valid JSON (single document) or strict JSONL (one JSON object per line); re-export if truncated.","Validate each line with JSON.parse before calling importTrace to locate all bad lines at once.","If the source is pretty-printed JSON, do not split it into lines yourself — pass it whole."],"exampleFix":"// before\nconst text = lines.join(''); // concatenated objects, invalid JSONL\nimportTrace(text);\n// after\nconst text = lines.filter(l => l.trim()).map(l => JSON.stringify(JSON.parse(l))).join('\\n');\nimportTrace(text);","handlingStrategy":"validation","validationCode":"function validateJsonOrJsonl(text: string): void {\n  try { JSON.parse(text); return; } catch {}\n  text.split(/\\r?\\n/).filter(l => l.trim()).forEach((l, i) => {\n    try { JSON.parse(l); } catch { throw new Error(`Invalid JSON on line ${i + 1}`); }\n  });\n}","typeGuard":"function isJsonLine(line: string): boolean {\n  try { JSON.parse(line); return true; } catch { return false; }\n}","tryCatchPattern":"try {\n  importTrace(text);\n} catch (e) {\n  const m = /Invalid JSON on nonempty line (\\d+)/.exec(e.message);\n  if (m) console.error(`Malformed JSONL at line ${m[1]}; fix or drop that line.`);\n}","preventionTips":["Keep exports as strict JSON (one document) or strict JSONL (one object per line).","Never split pretty-printed JSON into lines yourself.","Guard against truncated downloads: compare byte size or checksum with the exporter's report.","Pre-validate with a JSONL linter before importing."],"tags":["json","parsing","ingest"],"backgroundTag":"json-parse-error","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}