moeru-ai/airi · error
Failed to retweet: ${errorToMessage(error)}
Error message
Failed to retweet: ${errorToMessage(error)} What it means
This error wraps any failure that occurs during the retweet flow in the Twitter services module, which drives Twitter's web UI through a headless browser (Puppeteer-style page automation). The retweet sequence clicks the confirm button and then polls the timeline retweet button's aria-pressed attribute to verify success within a 5 second window. errorToMessage normalizes the underlying cause (timeout, navigation error, closed page, selector mismatch) into the thrown string.
Source
Thrown at integrations/twitter-services/src/core/services/tweet.ts:174
}
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)
throw new Error(`Failed to retweet: ${errorToMessage(error)}`)
}
}
/**
* Posts a new tweet with the given content and options
*/
async function postTweet(content: string, options: PostOptions = {}): Promise<string> {
try {
const page = ctx.page
// Go to home page where you can compose a tweet
await page.goto(TWITTER_HOME_URL)
// Wait for the tweet composer to load
await page.waitForSelector(SELECTORS.COMPOSE.TWEET_INPUT)
// Type the tweet content
await page.click(SELECTORS.COMPOSE.TWEET_INPUT)
await page.type(SELECTORS.COMPOSE.TWEET_INPUT, content)View on GitHub (pinned to 27111382b4)
Solutions
- Re-verify the session is authenticated (navigate to home, check for the compose box) before calling retweet.
- Confirm SELECTORS.TIMELINE.RETWEET_BUTTON and data-testid="retweetConfirm" still match the live DOM; update them if Twitter changed the UI.
- Raise the waitForFunction timeout above 5000ms or retry once on timeout, since retweets sometimes register slowly.
- Guard ctx.page for isClosed() before entry and re-create the page if it was torn down.
Example fix
// before
await page.waitForFunction(
`document.querySelector('${SELECTORS.TIMELINE.RETWEET_BUTTON}')?.getAttribute('aria-pressed') === 'true'`,
{ timeout: 5000 },
)
// after
await page.waitForFunction(
`document.querySelector('${SELECTORS.TIMELINE.RETWEET_BUTTON}')?.getAttribute('aria-pressed') === 'true'`,
{ timeout: 15000 },
).catch(async () => {
// re-check via the API-free heuristic: reload the tweet and inspect retweeted state
await page.reload({ waitUntil: 'networkidle' })
}) Defensive patterns
Strategy: try-catch
Validate before calling
if (!ctx.page || ctx.page.isClosed?.()) {
throw new Error('Page is not available; recreate it before retweeting')
}
// optional session pre-check
await ctx.page.goto(TWITTER_HOME_URL)
const loggedIn = await ctx.page.$('[data-testid="SideNav_NewTweet_Button"]')
if (!loggedIn) throw new Error('Session expired; re-authenticate before retweeting') Type guard
function isRetweetable(page: { click: (s: string) => Promise<unknown> } | null | undefined): page is { click: (s: string) => Promise<unknown> } {
return !!page && typeof page.click === 'function'
} Try / catch
try {
await retweet(tweetUrl)
}
catch (err) {
const msg = (err as Error).message
if (/timeout|aria-pressed/i.test(msg)) {
// transient: wait and retry once
await new Promise(r => setTimeout(r, 2000))
return retweet(tweetUrl)
}
if (/login|session/i.test(msg)) throw new Error('Re-auth required')
throw err
} Prevention
- Keep selectors in a single constants file and update them when Twitter ships UI changes.
- Refresh the session cookie on a schedule rather than waiting for a failure.
- Wrap each automation step in a timeout and surface a typed error so callers can retry vs re-auth.
When it happens
Trigger: Calling retweet(tweetUrl) when the session cookie is expired so Twitter shows a login wall instead of the retweet confirm; the data-testid="retweetConfirm" attribute was renamed by Twitter so page.click rejects; the waitForFunction exceeds its 5000ms timeout because aria-pressed never flips to 'true' (slow network, rate-limit interstitial, or already-retweeted state); ctx.page was closed or navigated away mid-flow.
Common situations: Twitter ships a DOM/A-B test that renames data-testid values; long-running bot sessions lose their auth cookie; running against a flaky proxy that stalls page loads; calling retweet on a tweet you already retweeted so the confirm path differs.
Related errors
- Failed to fetch profile for @${username}
- Failed to post tweet: ${errorToMessage(error)}
- Failed to like tweet: ${errorToMessage(error)}
- Tweet element not found
- Failed to extract tweet data
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/f223efc900801053.
Report an issue: GitHub.