{"record":{"id":"0ce73d39ee65914a","repo":"jackwener/OpenCLI","slug":"twitter-likes-resume-file-filepath-is-already-m","errorCode":null,"errorMessage":"Twitter likes resume file ${filePath} is already marked complete","messagePattern":"Twitter likes resume file (.+?) is already marked complete","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/likes.js","lineNumber":151,"sourceCode":"    if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)\n        || !Number.isInteger(count) || count < 0\n        || (parsed.cursor != null && typeof parsed.cursor !== 'string')\n        || (cursor !== null && !cursor.trim())) {\n        throw new CommandExecutionError(`Twitter likes resume file ${filePath} has an invalid shape`);\n    }\n    if (expected) {\n        if (parsed.source !== expected.source)\n            throw new ArgumentError(`Resume file source mismatch: expected ${expected.source}, found ${parsed.source || 'unknown'}`);\n        if (String(parsed.username || '').toLowerCase() !== String(expected.username).toLowerCase())\n            throw new ArgumentError(`Resume file username mismatch: expected @${expected.username}, found @${parsed.username || 'unknown'}`);\n        if (outputFile !== expected.outputFile)\n            throw new ArgumentError(`Resume file output mismatch: expected ${expected.outputFile || 'in-memory mode'}, found ${outputFile || 'in-memory mode'}`);\n        if (!expected.outputFile && !Array.isArray(parsed.tweets))\n            throw new CommandExecutionError(`Twitter likes resume file ${filePath} is missing in-memory tweets`);\n        if (!expected.outputFile && parsed.tweets.length !== count)\n            throw new CommandExecutionError(`Twitter likes resume file ${filePath} count does not match its in-memory tweets`);\n        if (parsed.complete)\n            throw new CommandExecutionError(`Twitter likes resume file ${filePath} is already marked complete`);\n    }\n    return {\n        cursor,\n        count,\n        tweets: Array.isArray(parsed.tweets) ? parsed.tweets : [],\n        username: parsed.username || null,\n        complete: Boolean(parsed.complete),\n        source: parsed.source || null,\n        outputFile,\n        updatedAt: parsed.updatedAt || null,\n    };\n}\n\n// Keep this in sync with twitter/bookmarks; wording is command-specific, so the\n// atomic tmp-write/rename cleanup sequence intentionally remains local.\nfunction writeResumeFile(filePath, payload) {\n    if (!filePath)\n        return;","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/likes.js#L133-L169","documentation":"When a Twitter likes archive finishes, the CLI marks its resume file with complete: true. readResumeFile rejects resume files already marked complete because there is nothing left to resume — the fetch already produced a full archive. Throwing prevents accidentally re-running a job that already succeeded.","triggerScenarios":"Re-running `opencli twitter likes <user> --all --resume-file <path>` after the previous run finished successfully and wrote complete: true into the resume file.","commonSituations":"User re-runs the same command out of habit or to 'refresh' data; a wrapper script retries the command after the first run already completed; cron job re-invoked after successful completion.","solutions":["Do not resume — the archive is complete; reuse the previously generated output archive.","If you truly want a fresh full fetch, delete the resume file (or point --resume-file at a new path) and re-run.","Check parsed.complete in the resume JSON before scripting retries; skip runs whose resume file is complete."],"exampleFix":"// before: shell retry loop resumes a completed job\nopencli twitter likes @jack --all --resume-file likes.json --output-file likes.ndjson\n// after: skip if already complete\njq -e '.complete == true' likes.json && echo 'already complete' || opencli twitter likes @jack --all --resume-file likes.json --output-file likes.ndjson","handlingStrategy":"validation","validationCode":"const state = JSON.parse(fs.readFileSync(resumePath, 'utf8'));\nif (state.complete) {\n  console.log('Archive already complete; skipping.');\n  process.exit(0);\n}","typeGuard":"function isResumable(state) {\n  return typeof state === 'object' && state !== null && state.complete !== true;\n}","tryCatchPattern":null,"preventionTips":["Check complete: true in the resume JSON before scheduling retries.","Delete the resume file when intentionally starting a fresh full fetch.","In cron/wrapper scripts, treat this error as 'success: already done' rather than retrying."],"tags":["twitter","resume-file","idempotency","cli"],"backgroundTag":"resume-already-complete","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}