moeru-ai/airi · error

Retweet button not found

Error message

Retweet button not found

What it means

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.

Source

Thrown at integrations/twitter-services/src/core/services/tweet.ts:155

    catch (error: unknown) {
      console.error('Error liking tweet:', error)
      throw new Error(`Failed to like tweet: ${errorToMessage(error)}`)
    }
  }

  /**
   * Retweets a tweet with the given ID
   */
  async function retweet(tweetId: string): Promise<boolean> {
    try {
      const page = ctx.page
      await page.goto(`${TWITTER_BASE_URL}/i/status/${tweetId}`)
      await page.waitForSelector(SELECTORS.TIMELINE.TWEET)

      // Click retweet button to open modal
      const retweetButton = await page.$(SELECTORS.TIMELINE.RETWEET_BUTTON)
      if (!retweetButton) {
        throw new Error('Retweet button not found')
      }

      await retweetButton.click()

      // Wait for retweet confirmation dialog and click it
      await page.waitForSelector('[data-testid="retweetConfirm"]')
      await page.click('[data-testid="retweetConfirm"]')

      // Wait for the retweet to register
      await page.waitForFunction(
        `document.querySelector('${SELECTORS.TIMELINE.RETWEET_BUTTON}')?.getAttribute('aria-pressed') === 'true'`,
        { timeout: 5000 },
      )

      return true
    }
    catch (error: unknown) {
      console.error('Error retweeting:', error)

View on GitHub (pinned to 27111382b4)

Solutions

  1. Update SELECTORS.TIMELINE.RETWEET_BUTTON to the current X data-testid.
  2. Verify the tweetId is valid, existing, and retweetable by the session account.
  3. Ensure the session is authenticated and not limited/view-only.
  4. Add a waitForSelector(RETWEET_BUTTON) with a timeout before the page.$ to absorb render lag.

Example fix

// before
const retweetButton = await page.$(SELECTORS.TIMELINE.RETWEET_BUTTON)
if (!retweetButton) {
  throw new Error('Retweet button not found')
}

// after
await page.waitForSelector(SELECTORS.TIMELINE.RETWEET_BUTTON, { timeout: 5000 })
const retweetButton = await page.$(SELECTORS.TIMELINE.RETWEET_BUTTON)
if (!retweetButton) {
  throw new Error('Retweet button not found (tweet may be unavailable)')
}
Defensive patterns

Strategy: validation

Validate before calling

await page.waitForSelector(SELECTORS.TIMELINE.RETWEET_BUTTON, { timeout: 5000 })
const retweetButton = await page.$(SELECTORS.TIMELINE.RETWEET_BUTTON)
if (!retweetButton) throw new Error('Retweet button not found (tweet may be unavailable)')

Type guard

function isRetweetButtonNotFoundError(e: unknown): boolean {
  // Note: retweet wraps this into 'Failed to retweet: Retweet button not found'
  return e instanceof Error && /Retweet button not found/.test(e.message)
}

Try / catch

try {
  await tweetServices.tweet.retweet(tweetId)
} catch (e) {
  if (e instanceof Error && /Retweet button not found/.test(e.message)) {
    // update SELECTORS.TIMELINE.RETWEET_BUTTON or verify the tweet is retweetable
  } else throw e
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12). Data as JSON: /api/errors/543af65c53cdb23e. Report an issue: GitHub.