{"record":{"id":"cec80fa6bc3f784a","repo":"jackwener/OpenCLI","slug":"twitter-likes-output-file-has-jsonlstate-count","errorCode":null,"errorMessage":"Twitter likes output file has ${jsonlState.count} record(s), expected resume count ${resumed.count}","messagePattern":"Twitter likes output file has (.+?) record\\(s\\), expected resume count (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/likes.js","lineNumber":288,"sourceCode":"        }\n        const resumed = fetchAll ? readResumeFile(resumeFile, {\n            source: 'likes',\n            username,\n            outputFile: useOutputFile ? outputFile : null,\n        }) : null;\n        if (useOutputFile && resumed && resumed.count > 0 && !fs.existsSync(outputFile)) {\n            throw new CommandExecutionError(`Twitter likes output file is missing for resume state: ${outputFile}`);\n        }\n        if (useOutputFile && !resumed && fs.existsSync(outputFile)) {\n            throw new ArgumentError(`Refusing to overwrite existing Twitter likes output file: ${outputFile}`);\n        }\n        const allTweets = useOutputFile ? [] : (resumed?.tweets ? [...resumed.tweets] : []);\n        const jsonlState = useOutputFile ? loadJsonlArchiveState(outputFile) : null;\n        const seen = useOutputFile\n            ? jsonlState.seen\n            : new Set(allTweets.map((tweet) => tweet?.id).filter(Boolean));\n        if (useOutputFile && resumed && jsonlState.count !== resumed.count) {\n            throw new CommandExecutionError(`Twitter likes output file has ${jsonlState.count} record(s), expected resume count ${resumed.count}`);\n        }\n        let outputCount = useOutputFile ? jsonlState.count : 0;\n        let cursor = resumed?.cursor || null;\n        let lastRawResponse = null;\n        let pages = 0;\n        let exhausted = false;\n        // Runaway guard only; --limit/--all and cursor exhaustion control normal pagination.\n        while (pages < maxPages && (fetchAll || allTweets.length < limit)) {\n            pages += 1;\n            const currentCount = useOutputFile ? outputCount : allTweets.length;\n            const remaining = fetchAll ? 100 : (limit - currentCount + 10);\n            const fetchCount = Math.min(100, remaining);\n            const apiUrl = buildLikesUrl(likesQueryId, userId, fetchCount, cursor);\n            const data = unwrapBrowserResult(await page.evaluate(`async () => {\n        const r = await fetch(${JSON.stringify(apiUrl)}, { headers: ${headers}, credentials: 'include' });\n        return r.ok ? await r.json() : { error: r.status };\n      }`));\n            if (data?.error) {","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/likes.js#L270-L306","documentation":"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.","triggerScenarios":"useOutputFile is true, a resume file exists, and loadJsonlArchiveState(outputFile).count !== resumed.count when evaluated at clis/twitter/likes.js:288.","commonSituations":"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).","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."],"exampleFix":"// before\n$ cli twitter likes @user --all --resume  # jsonl has 100 rows, resume says 120\n// after\n$ rm likes.jsonl .twitter-likes-user.resume\n$ cli twitter likes @user --all --output likes.jsonl","handlingStrategy":"validation","validationCode":"const fs = require('node:fs');\nconst rows = fs.existsSync(outputFile)\n  ? fs.readFileSync(outputFile, 'utf8').split('\\n').filter(Boolean).length : 0;\nconst expected = readResumeCount(resumeFile);\nif (expected != null && rows !== expected) {\n  fs.rmSync(resumeFile); fs.rmSync(outputFile); // reset inconsistent state\n}","typeGuard":"null","tryCatchPattern":"try {\n  await cli.twitter.likes({ username, all: true, resume: true });\n} catch (e) {\n  if (/expected resume count/.test(e.message)) {\n    resetResumeAndOutput(); // delete both, start fresh\n  } else throw e;\n}","preventionTips":["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"],"tags":["resume","data-integrity","archive"],"backgroundTag":"resume-state-mismatch","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}