jackwener/OpenCLI · error · CommandExecutionError
Twitter likes output file has ${jsonlState.count} record(s),
Error message
Twitter likes output file has ${jsonlState.count} record(s), expected resume count ${resumed.count} What it means
CommandExecutionError thrown when the on-disk JSONL output contains a different number of records than the resume state file claims. This guards against truncated or hand-edited archives: resuming would produce a corrupt, gapped, or duplicated likes archive.
Source
Thrown at clis/twitter/likes.js:288
}
const resumed = fetchAll ? readResumeFile(resumeFile, {
source: 'likes',
username,
outputFile: useOutputFile ? outputFile : null,
}) : null;
if (useOutputFile && resumed && resumed.count > 0 && !fs.existsSync(outputFile)) {
throw new CommandExecutionError(`Twitter likes output file is missing for resume state: ${outputFile}`);
}
if (useOutputFile && !resumed && fs.existsSync(outputFile)) {
throw new ArgumentError(`Refusing to overwrite existing Twitter likes output file: ${outputFile}`);
}
const allTweets = useOutputFile ? [] : (resumed?.tweets ? [...resumed.tweets] : []);
const jsonlState = useOutputFile ? loadJsonlArchiveState(outputFile) : null;
const seen = useOutputFile
? jsonlState.seen
: new Set(allTweets.map((tweet) => tweet?.id).filter(Boolean));
if (useOutputFile && resumed && jsonlState.count !== resumed.count) {
throw new CommandExecutionError(`Twitter likes output file has ${jsonlState.count} record(s), expected resume count ${resumed.count}`);
}
let outputCount = useOutputFile ? jsonlState.count : 0;
let cursor = resumed?.cursor || null;
let lastRawResponse = null;
let pages = 0;
let exhausted = false;
// Runaway guard only; --limit/--all and cursor exhaustion control normal pagination.
while (pages < maxPages && (fetchAll || allTweets.length < limit)) {
pages += 1;
const currentCount = useOutputFile ? outputCount : allTweets.length;
const remaining = fetchAll ? 100 : (limit - currentCount + 10);
const fetchCount = Math.min(100, remaining);
const apiUrl = buildLikesUrl(likesQueryId, userId, fetchCount, cursor);
const data = unwrapBrowserResult(await page.evaluate(`async () => {
const r = await fetch(${JSON.stringify(apiUrl)}, { headers: ${headers}, credentials: 'include' });
return r.ok ? await r.json() : { error: r.status };
}`));
if (data?.error) {View on GitHub (pinned to 49907e53dc)
Solutions
- Delete both the resume file and the mismatched output file, then re-run from scratch.
- Restore the JSONL from a backup so its row count matches the resume state.
- Manually trim the resume state's recorded count only if you understand the rows are valid (advanced).
- Re-run the fetch fresh into a new --output path.
Example fix
// before $ cli twitter likes @user --all --resume # jsonl has 100 rows, resume says 120 // after $ rm likes.jsonl .twitter-likes-user.resume $ cli twitter likes @user --all --output likes.jsonl
Defensive patterns
Strategy: validation
Validate before calling
const fs = require('node:fs');
const rows = fs.existsSync(outputFile)
? fs.readFileSync(outputFile, 'utf8').split('\n').filter(Boolean).length : 0;
const expected = readResumeCount(resumeFile);
if (expected != null && rows !== expected) {
fs.rmSync(resumeFile); fs.rmSync(outputFile); // reset inconsistent state
} Type guard
null
Try / catch
try {
await cli.twitter.likes({ username, all: true, resume: true });
} catch (e) {
if (/expected resume count/.test(e.message)) {
resetResumeAndOutput(); // delete both, start fresh
} else throw e;
} Prevention
- Never hand-edit a JSONL archive that has active resume state
- Ensure the process isn't killed mid-append (use graceful shutdown)
- Keep output and resume files as an atomic pair when copying/backing up
- Check disk space before long archive fetches
When it happens
Trigger: useOutputFile is true, a resume file exists, and loadJsonlArchiveState(outputFile).count !== resumed.count when evaluated at clis/twitter/likes.js:288.
Common situations: The JSONL was truncated by a crash/disk-full mid-append; someone edited or filtered the output file manually; output and resume file pair got out of sync (mixed from different runs).
Related errors
- twitter_bookmarks_archive_incomplete
- archive wayback failed: HTTP ${resp.status}
- archive wayback returned malformed JSON: ${error?.message ||
- No Wayback snapshot for "${target}".
- archive wayback returned malformed payload: closest snapshot
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/cec80fa6bc3f784a.
Report an issue: GitHub.