{"record":{"id":"2d4ed19f11a9eb45","repo":"jackwener/OpenCLI","slug":"refusing-to-overwrite-existing-twitter-bookmarks-o","errorCode":null,"errorMessage":"Refusing to overwrite existing Twitter bookmarks output file: ${outputFile}","messagePattern":"Refusing to overwrite existing Twitter bookmarks output file: (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/twitter/bookmarks.js","lineNumber":223,"sourceCode":"        const ct0 = cookies.find((c) => c.name === 'ct0')?.value || null;\n        if (!ct0)\n            throw new AuthRequiredError('x.com', 'Not logged into x.com (no ct0 cookie)');\n        const queryId = await resolveTwitterQueryId(page, 'Bookmarks', BOOKMARKS_QUERY_ID);\n        const headers = JSON.stringify({\n            'Authorization': `Bearer ${decodeURIComponent(TWITTER_BEARER_TOKEN)}`,\n            'X-Csrf-Token': ct0,\n            'X-Twitter-Auth-Type': 'OAuth2Session',\n            'X-Twitter-Active-User': 'yes',\n        });\n        const resumed = fetchAll ? readResumeFile(resumeFile, {\n            source: 'bookmarks',\n            outputFile: useOutputFile ? outputFile : null,\n        }) : null;\n        if (useOutputFile && resumed && resumed.count > 0 && !fs.existsSync(outputFile)) {\n            throw new CommandExecutionError(`Twitter bookmarks 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 bookmarks 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 bookmarks 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 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);","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/bookmarks.js#L205-L241","documentation":"To avoid destroying existing archive data, the CLI refuses to start a fresh (non-resumed) archive run when the output file already exists. clis/twitter/bookmarks.js:223 throws ArgumentError when useOutputFile is true, there is no valid resume state (resumed is null), and fs.existsSync(outputFile) is true.","triggerScenarios":"Running `twitter bookmarks --all --output-file existing.jsonl --resume-file new.resume.json` where the resume file does not exist (fresh run) but the output file does.","commonSituations":"Re-running a completed archive without the resume file (resume was removed on success); pointing --output-file at a different command's file; switching output filenames mid-workflow so old resume no longer matches.","solutions":["Point --output-file at a new file path if you intend a fresh archive.","Resume the previous run instead: pass the matching --resume-file alongside the existing --output-file.","Explicitly delete/rename the existing output file if you are certain it should be overwritten.","Keep the resume file until the archive completes (it is auto-removed on success) so reruns resume rather than clash."],"exampleFix":"// before\ntwitter bookmarks --all --output-file bookmarks.jsonl --resume-file fresh.resume.json  # fresh run, file exists\n// after\ntwitter bookmarks --all --output-file bookmarks.jsonl --resume-file bookmarks.resume.json  # matching resume","handlingStrategy":"validation","validationCode":"if (!fs.existsSync(resumeFile) && fs.existsSync(outputFile)) {\n  throw new Error(`Refusing to overwrite existing output file: ${outputFile} — resume first or choose a new path`);\n}","typeGuard":"function isSafeFreshArchiveTarget(resumeFile, outputFile) {\n  return fs.existsSync(resumeFile) || !fs.existsSync(outputFile);\n}","tryCatchPattern":"try {\n  await runBookmarks(argv);\n} catch (e) {\n  if (e instanceof ArgumentError && e.message.includes('Refusing to overwrite')) {\n    console.error('Pick a new --output-file or supply the matching --resume-file to continue.');\n    process.exitCode = 2;\n  } else throw e;\n}","preventionTips":["Keep the resume file until the archive completes; it is auto-removed on success.","Use unique, dated output filenames for fresh archives.","Check fs.existsSync(outputFile) in wrappers before a fresh run.","Never reuse an --output-file path across different archive projects."],"tags":["cli","filesystem","data-loss-prevention"],"backgroundTag":"refusing-to-overwrite-file","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}