jackwener/OpenCLI · error · CommandExecutionError

Invalid JSONL record in ${filePath} at line ${index + 1}: ${

Error message

Invalid JSONL record in ${filePath} at line ${index + 1}: ${error instanceof Error ? error.message : String(error)}

What it means

loadJsonlArchiveState reads a JSONL file line by line; each record must parse and contain a valid, non-duplicate id. Any per-line failure (JSON syntax error, missing id, duplicate id) is rethrown as a CommandExecutionError naming the file, the 1-based line number, and the underlying cause. This pinpoints exactly which archive line is corrupt so resume state can be repaired.

Source

Thrown at clis/twitter/archive.js:55

    if (!filePath || !fs.existsSync(filePath))
        return { seen, count };
    const text = fs.readFileSync(filePath, 'utf8');
    for (const [index, line] of text.split('\n').entries()) {
        const trimmed = line.trim();
        if (!trimmed)
            continue;
        try {
            const row = JSON.parse(trimmed);
            if (!row?.id)
                throw new Error('missing id');
            const id = String(row.id);
            if (seen.has(id))
                throw new Error(`duplicate id ${id}`);
            seen.add(id);
            count += 1;
        }
        catch (error) {
            throw new CommandExecutionError(`Invalid JSONL record in ${filePath} at line ${index + 1}: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    return { seen, count };
}

export function appendJsonlRows(filePath, rows) {
    if (!filePath || !Array.isArray(rows) || rows.length === 0)
        return;
    ensureParentDir(filePath);
    // Escape LS/PS so JSONL stays one physical line even when tweet text contains them.
    const text = rows
        .map((row) => JSON.stringify(row).replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029'))
        .join('\n') + '\n';
    fs.appendFileSync(filePath, text, 'utf8');
}

export function removeResumeFile(filePath) {
    removeFile(filePath);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Open the file at the reported line number and fix or delete the malformed record
  2. If the last line is truncated (killed process), remove just that partial line — earlier records remain valid
  3. Deduplicate repeated id lines or restore the file from a backup
  4. If untrusted, delete the state file and rerun so a fresh archive is written

Example fix

// before (corrupt line 3)
{"id":"12"}
{"id":"13"
// after (truncated line removed)
{"id":"12"}
{"id":"13"}
Defensive patterns

Strategy: validation

Validate before calling

import { readFileSync } from 'node:fs';
function validateJsonl(filePath) {
  const seen = new Set();
  readFileSync(filePath, 'utf8').split('\n').forEach((line, i) => {
    if (!line.trim()) return;
    const rec = JSON.parse(line); // throws with line info on corrupt data
    if (seen.has(rec.id)) throw new Error(`duplicate id ${rec.id} at line ${i + 1}`);
    seen.add(rec.id);
  });
}

Type guard

function isValidRecord(rec) { return rec !== null && typeof rec === 'object' && typeof rec.id === 'string' && rec.id.length > 0; }

Try / catch

try {
  const state = await loadJsonlArchiveState(filePath);
} catch (err) {
  const m = /line (\d+)/.exec(err.message);
  if (m) console.error(`Fix or delete line ${m[1]} of ${filePath}, then rerun`);
  else throw err;
}

Prevention

When it happens

Trigger: The resume/state JSONL file contains a line that fails JSON.parse, lacks an id field, or repeats an id already seen — thrown from the state or jsonlState commands.

Common situations: Manually editing the JSONL file and breaking syntax, a previous run being killed mid-write leaving a truncated last line, file corruption from disk issues, or concatenating two archive files producing duplicate ids.

Understand the failure class

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/1892fd511576f3b8. Report an issue: GitHub.