{"record":{"id":"fff4b72b3da0acf0","repo":"jackwener/OpenCLI","slug":"could-not-persist-twitter-bookmarks-resume-state","errorCode":null,"errorMessage":"Could not persist Twitter bookmarks resume state: ${error instanceof Error ? error.message : String(error)}","messagePattern":"Could not persist Twitter bookmarks resume state: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/bookmarks.js","lineNumber":164,"sourceCode":"\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);\n    const temporaryPath = `${filePath}.tmp-${process.pid}`;\n    try {\n        fs.writeFileSync(temporaryPath, JSON.stringify(payload, null, 2) + '\\n');\n        fs.renameSync(temporaryPath, filePath);\n    }\n    catch (error) {\n        try {\n            fs.rmSync(temporaryPath, { force: true });\n        }\n        catch {\n        }\n        throw new CommandExecutionError(`Could not persist Twitter bookmarks resume state: ${error instanceof Error ? error.message : String(error)}`);\n    }\n}\ncli({\n    site: 'twitter',\n    name: 'bookmarks',\n    access: 'read',\n    description: 'Fetch your Twitter/X bookmarks (the logged-in user\\'s saved tweets, newest first)',\n    domain: 'x.com',\n    strategy: Strategy.COOKIE,\n    browser: true,\n    args: [\n        { name: 'limit', type: 'int', default: 20, help: 'Maximum number of bookmarks to return (default 20). Ignored when --all is set.' },\n        { name: 'all', type: 'bool', default: false, help: 'Fetch all bookmark pages until exhausted. Prefer --output-file for large archives.' },\n        { name: 'resume-file', type: 'string', help: 'Resume file for long-running all-pages bookmark syncs.' },\n        { name: 'output-file', type: 'string', help: 'Write all-page results to JSONL. Requires --all and --resume-file.' },\n        { name: 'max-pages', type: 'int', help: `Optional pagination safety cap (default ${DEFAULT_MAX_PAGINATION_PAGES}; raised automatically with --all).` },\n        { name: 'top-by-engagement', type: 'int', default: 0, help: 'When set to N>0, re-rank the bookmarks by weighted engagement (likes×1 + retweets×3 + replies×2 + bookmarks×5 + log10(views+1)×0.5) and return the top N. Default 0 keeps the API\\'s native (saved-time) ordering. Incompatible with --output-file.' },\n    ],","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/bookmarks.js#L146-L182","documentation":"writeResumeFile persists state atomically: it writes `${filePath}.tmp-${pid}` then renames onto the resume file. If the write or rename fails, it removes the temp file and throws this CommandExecutionError wrapping the underlying message (ENOENT, EACCES, ENOSPC, EPERM, etc.).","triggerScenarios":"Resume-file directory does not exist and cannot be created (permissions); disk full (ENOSPC); read-only filesystem; the target path is a directory; another process/AV locks the file during rename on some platforms.","commonSituations":"--resume-file pointing under a root-owned or read-only directory; container volumes mounted read-only; quota exceeded on a long --all sync; resume path inside a deleted working directory.","solutions":["Check disk space (`df -h`) and free space if ENOSPC.","Verify the resume-file's parent directory exists and is writable by the current user (mkdir/chown/chmod).","Point --resume-file at a writable path (e.g. $HOME or a writable volume).","If a stale `.tmp-<pid>` file exists and is locked, remove it and retry; on Windows, close tools holding the file."],"exampleFix":"// before: fails\ncli twitter bookmarks --all --resume-file /proc/run/b.json\n// EACCES/ENOENT -> Could not persist Twitter bookmarks resume state\n// after:\nmkdir -p ~/var/lib/twitter-sync\ncli twitter bookmarks --all --resume-file ~/var/lib/twitter-sync/bookmarks.json --output-file ~/var/lib/twitter-sync/archive.jsonl","handlingStrategy":"try-catch","validationCode":"const dir = path.dirname(resumeFile);\nfs.mkdirSync(dir, { recursive: true });\nfs.accessSync(dir, fs.constants.W_OK);\n// also check free space if the sync is large\nconst stat = fs.statfsSync(dir);\nif (stat.bsize * stat.bavail < 10 * 1024 * 1024) throw new Error('low disk space');","typeGuard":"null","tryCatchPattern":"try {\n  await run(['twitter', 'bookmarks', '--all', '--resume-file', p, '--output-file', o]);\n} catch (e) {\n  if (String(e.message).includes('Could not persist Twitter bookmarks resume state')) {\n    console.error('resume write failed:', e.message);\n    const writable = path.join(os.tmpdir(), 'bookmarks-resume.json');\n    await run(['twitter', 'bookmarks', '--all', '--resume-file', writable, '--output-file', o]);\n  } else throw e;\n}","preventionTips":["Point --resume-file at a writable, non-volatile directory ($HOME, writable volume).","Monitor disk space before long --all syncs.","Avoid read-only mounts and root-owned paths for state files.","Clean stale `.tmp-<pid>` files after crashes."],"tags":["filesystem","io-error","resume-file"],"backgroundTag":"file-write-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}