{"record":{"id":"38a65b3a90a0cf66","repo":"jackwener/OpenCLI","slug":"nothing-changed-open-the-tweet-in-the-browser-and","errorCode":null,"errorMessage":"Nothing changed. Open the tweet in the browser and retry.","messagePattern":"Nothing changed\\. Open the tweet in the browser and retry\\.","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/delete.js","lineNumber":88,"sourceCode":"    strategy: Strategy.UI, // Utilizes internal DOM flows for interaction\n    browser: true,\n    args: [\n        { name: 'url', type: 'string', required: true, positional: true, help: 'The URL of the tweet to delete' },\n    ],\n    columns: ['status', 'message'],\n    func: async (page, kwargs) => {\n        if (!page)\n            throw new CommandExecutionError('Browser session required for twitter delete');\n        // parseTweetUrl throws ArgumentError on malformed/off-domain inputs —\n        // this replaces the ad-hoc local extractTweetId which only checked\n        // the path shape and accepted any host (silent: would try to act on\n        // attacker-controlled redirect URLs).\n        const target = parseTweetUrl(kwargs.url);\n        await page.goto(target.url);\n        await page.wait({ selector: '[data-testid=\"primaryColumn\"]' }); // Wait for tweet to load completely\n        const result = unwrapBrowserResult(await page.evaluate(buildDeleteScript(target.id)));\n        if (!result.ok) {\n            throw new CommandExecutionError(result.message, 'Nothing changed. Open the tweet in the browser and retry.');\n        }\n        await page.wait(2);\n        return [{\n                status: 'success',\n                message: result.message\n            }];\n    }\n});\nexport const __test__ = {\n    buildDeleteScript,\n};\n","sourceCodeStart":70,"sourceCodeEnd":100,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/delete.js#L70-L100","documentation":"After loading the tweet page and executing buildDeleteScript, the library checks result.ok from the in-page script. If the delete did not succeed (X API call inside the page failed, tweet already gone, permissions issue, UI changed), it throws with the script's message and the hint that nothing was changed, so the user knows the tweet still exists.","triggerScenarios":"page.evaluate's delete script returns ok:false — e.g. the tweet was already deleted, the logged-in account lacks permission (not the author), X's internal mutation endpoint rejected the request (rate limit, CSRF token mismatch), or the DOM/protocol changed so the script failed to complete.","commonSituations":"Deleting a tweet belonging to a different account; double-running a delete after a prior success; X frontend update changing the delete mutation or its tokens; session cookies stale so the in-page API call returns unauthorized; rate limiting on rapid successive deletions.","solutions":["Open the tweet URL in the browser and attempt deletion manually — the message says nothing changed, so retry once the session/permissions are confirmed.","Verify the logged-in account is the tweet's author and the tweet still exists (404s and foreign tweets both fail).","Refresh login/session cookies if the in-page mutation returns unauthorized.","Update the library if X changed its delete mutation protocol (script may target stale endpoints).","If bulk-deleting, add delays between deletions to avoid X rate limits rejecting the mutation."],"exampleFix":"// before\nconst result = unwrapBrowserResult(await page.evaluate(buildDeleteScript(target.id)));\nif (!result.ok) throw new CommandExecutionError(result.message, '...retry.');\n// after\nconst result = unwrapBrowserResult(await page.evaluate(buildDeleteScript(target.id)));\nif (!result.ok) {\n  if (/already|not found/i.test(result.message)) return [{ status: 'already-deleted', message: result.message }];\n  throw new CommandExecutionError(result.message, 'Nothing changed. Open the tweet in the browser and retry.');\n}","handlingStrategy":"try-catch","validationCode":"// before deleting, confirm the tweet exists and belongs to the session's account\nawait page.goto(target.url);\nawait page.wait({ selector: '[data-testid=\"primaryColumn\"]' });\nif (await page.evaluate(() => document.body.innerText.includes('This post was deleted'))) {\n  return; // already gone\n}","typeGuard":"function isSuccessfulDelete(result) {\n  return Boolean(result && result.ok === true && typeof result.message === 'string');\n}","tryCatchPattern":"try {\n  await cliRun('twitter delete', { url });\n} catch (err) {\n  if (/Nothing changed/.test(err.message)) {\n    // verify authorship + existence, refresh session, retry once manually\n  } else throw err;\n}","preventionTips":["Only delete tweets owned by the logged-in account.","Check the tweet still exists before invoking the delete script.","Refresh session cookies if in-page mutations return unauthorized.","Add delays between bulk deletions to avoid X rate limits.","Update the library after X frontend changes alter the delete mutation.","Verify deletion by reloading the tweet URL after the command."],"tags":["twitter","delete","browser","mutation-failed"],"backgroundTag":"page-action-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}