{"record":{"id":"7a3e3cddd4fc6ad3","repo":"jackwener/OpenCLI","slug":"twitter-bookmarks-resume-file-filepath-is-alrea","errorCode":null,"errorMessage":"Twitter bookmarks resume file ${filePath} is already marked complete","messagePattern":"Twitter bookmarks resume file (.+?) is already marked complete","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/bookmarks.js","lineNumber":134,"sourceCode":"    const cursor = parsed?.cursor == null ? null : String(parsed.cursor);\n    const outputFile = parsed?.outputFile ? path.resolve(String(parsed.outputFile)) : null;\n    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 bookmarks 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 (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 bookmarks resume file ${filePath} is missing in-memory tweets`);\n        if (!expected.outputFile && parsed.tweets.length !== count)\n            throw new CommandExecutionError(`Twitter bookmarks resume file ${filePath} count does not match its in-memory tweets`);\n        if (parsed.complete)\n            throw new CommandExecutionError(`Twitter bookmarks resume file ${filePath} is already marked complete`);\n    }\n    return {\n        cursor,\n        count,\n        tweets: Array.isArray(parsed.tweets) ? parsed.tweets : [],\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/likes; 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;\n    ensureParentDir(filePath);","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/bookmarks.js#L116-L152","documentation":"A resume file whose `complete` flag is true records a fully finished sync. readResumeFile refuses to resume such a file and throws this CommandExecutionError so a completed archive is not re-scrolled or overwritten.","triggerScenarios":"Re-running `twitter bookmarks --all --resume-file <path>` after a previous run finished successfully and marked `complete: true` in the file.","commonSituations":"Running the same command twice to 'refresh' data; a cron/job retrying after completion; forgetting the previous run already finished.","solutions":["Nothing to resume — read the completed JSONL archive (--output-file) or the previous results instead.","Delete the completed resume file (and optionally keep the JSONL) before starting a fresh full sync.","Check `parsed.complete` in the file first to detect an already-finished run."],"exampleFix":"// before:\ncli twitter bookmarks --all --resume-file ./b.json  # file has complete: true\n// after: archive done; for a fresh run:\nrm ./b.json && cli twitter bookmarks --all --resume-file ./b.json","handlingStrategy":"validation","validationCode":"const parsed = JSON.parse(fs.readFileSync(resumeFile, 'utf8'));\nif (parsed.complete) {\n  // already done: consume the JSONL archive instead of resuming\n  console.log('sync already complete; read', parsed.outputFile);\n}","typeGuard":"function isCompletedResume(v) {\n  return typeof v === 'object' && v !== null && v.complete === true;\n}","tryCatchPattern":"try {\n  await run(args);\n} catch (e) {\n  if (String(e.message).includes('is already marked complete')) {\n    // no-op: previous run finished; read the output-file instead\n  } else throw e;\n}","preventionTips":["Before launching scheduled --all runs, check `complete` in the resume file.","Delete the resume file (keep the JSONL) when a completed archive is done.","Make cron jobs idempotent: skip if complete=true or the archive has a done-marker.","Archive the finished JSONL and reset state before a new full sync."],"tags":["resume-file","already-complete","cli"],"backgroundTag":"resume-file-already-complete","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}