{"record":{"id":"543af65c53cdb23e","repo":"moeru-ai/airi","slug":"retweet-button-not-found","errorCode":null,"errorMessage":"Retweet button not found","messagePattern":"Retweet button not found","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"integrations/twitter-services/src/core/services/tweet.ts","lineNumber":155,"sourceCode":"    catch (error: unknown) {\n      console.error('Error liking tweet:', error)\n      throw new Error(`Failed to like tweet: ${errorToMessage(error)}`)\n    }\n  }\n\n  /**\n   * Retweets a tweet with the given ID\n   */\n  async function retweet(tweetId: string): Promise<boolean> {\n    try {\n      const page = ctx.page\n      await page.goto(`${TWITTER_BASE_URL}/i/status/${tweetId}`)\n      await page.waitForSelector(SELECTORS.TIMELINE.TWEET)\n\n      // Click retweet button to open modal\n      const retweetButton = await page.$(SELECTORS.TIMELINE.RETWEET_BUTTON)\n      if (!retweetButton) {\n        throw new Error('Retweet button not found')\n      }\n\n      await retweetButton.click()\n\n      // Wait for retweet confirmation dialog and click it\n      await page.waitForSelector('[data-testid=\"retweetConfirm\"]')\n      await page.click('[data-testid=\"retweetConfirm\"]')\n\n      // Wait for the retweet to register\n      await page.waitForFunction(\n        `document.querySelector('${SELECTORS.TIMELINE.RETWEET_BUTTON}')?.getAttribute('aria-pressed') === 'true'`,\n        { timeout: 5000 },\n      )\n\n      return true\n    }\n    catch (error: unknown) {\n      console.error('Error retweeting:', error)","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/integrations/twitter-services/src/core/services/tweet.ts#L137-L173","documentation":"Thrown by retweet when page.$(SELECTORS.TIMELINE.RETWEET_BUTTON) returns null — the tweet page rendered (waitForSelector for the tweet passed) but the retweet button element was not found. This is a plain Error thrown inside the try, so it is subsequently re-wrapped by the 'Failed to retweet' catch in the same function.","triggerScenarios":"Navigating to /i/status/<tweetId> where the tweet node renders but SELECTORS.TIMELINE.RETWEET_BUTTON does not match: stale selector after an X UI change; the tweet is protected/deleted/unretweetable so the action row is absent; the page shows an unavailable-post state; permission/session limits hide the button.","commonSituations":"X redesigned the action bar so the data-testid changed (commonly '[data-testid=\"retweet\"]'); tweetId is invalid or points to a deleted/protected post; the session cannot retweet (view-only); protected accounts hide the retweet control.","solutions":["Update SELECTORS.TIMELINE.RETWEET_BUTTON to the current X data-testid.","Verify the tweetId is valid, existing, and retweetable by the session account.","Ensure the session is authenticated and not limited/view-only.","Add a waitForSelector(RETWEET_BUTTON) with a timeout before the page.$ to absorb render lag."],"exampleFix":"// before\nconst retweetButton = await page.$(SELECTORS.TIMELINE.RETWEET_BUTTON)\nif (!retweetButton) {\n  throw new Error('Retweet button not found')\n}\n\n// after\nawait page.waitForSelector(SELECTORS.TIMELINE.RETWEET_BUTTON, { timeout: 5000 })\nconst retweetButton = await page.$(SELECTORS.TIMELINE.RETWEET_BUTTON)\nif (!retweetButton) {\n  throw new Error('Retweet button not found (tweet may be unavailable)')\n}","handlingStrategy":"validation","validationCode":"await page.waitForSelector(SELECTORS.TIMELINE.RETWEET_BUTTON, { timeout: 5000 })\nconst retweetButton = await page.$(SELECTORS.TIMELINE.RETWEET_BUTTON)\nif (!retweetButton) throw new Error('Retweet button not found (tweet may be unavailable)')","typeGuard":"function isRetweetButtonNotFoundError(e: unknown): boolean {\n  // Note: retweet wraps this into 'Failed to retweet: Retweet button not found'\n  return e instanceof Error && /Retweet button not found/.test(e.message)\n}","tryCatchPattern":"try {\n  await tweetServices.tweet.retweet(tweetId)\n} catch (e) {\n  if (e instanceof Error && /Retweet button not found/.test(e.message)) {\n    // update SELECTORS.TIMELINE.RETWEET_BUTTON or verify the tweet is retweetable\n  } else throw e\n}","preventionTips":["Update RETWEET_BUTTON selector to the current X data-testid when the UI changes.","Verify the tweetId is valid, existing, and retweetable before retweeting.","Ensure the session is authenticated and not view-only.","waitForSelector on the retweet button before querying it to absorb render lag."],"tags":["twitter","puppeteer","selector","retweet","selector-drift"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}