moeru-ai/airi · error
Failed to fetch profile for @${username}
Error message
Failed to fetch profile for @${username} What it means
This is the catch-all for getUserProfile, which navigates to /<username>, waits for [data-testid="UserName"], and scrapes display name, bio, avatar, and follower counts. Any failure during that sequence is logged and rethrown as a profile-specific error that does not include the underlying cause text.
Source
Thrown at integrations/twitter-services/src/core/services/user.ts:89
? Number.parseInt((await followElement.textContent() || '0').replace(/\D/g, ''))
: undefined
const followingCount = followingElement
? Number.parseInt((await followingElement.textContent() || '0').replace(/\D/g, ''))
: undefined
return {
username,
displayName,
bio: bio || undefined,
avatarUrl: avatarUrl || undefined,
followersCount: followerCount || undefined,
followingCount: followingCount || undefined,
}
}
catch (error) {
logger.main.error('Error fetching user profile:', (error as Error).message)
throw new Error(`Failed to fetch profile for @${username}`)
}
}
return {
followUser,
getUserProfile,
}
}
View on GitHub (pinned to 27111382b4)
Solutions
- Verify the username exists and is viewable by the authenticated session before scraping.
- Confirm [data-testid="UserName"] and the follower/following count selectors still match the live profile.
- After goto, detect a 404/suspended/login page and throw a more specific error.
- Include the underlying cause in the rethrown error so diagnosis is possible.
Example fix
// before
catch (error) {
logger.main.error('Error fetching user profile:', (error as Error).message)
throw new Error(`Failed to fetch profile for @${username}`)
}
// after
catch (error) {
const cause = (error as Error).message
logger.main.error('Error fetching user profile:', cause)
throw new Error(`Failed to fetch profile for @${username}: ${cause}`)
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!/^[A-Za-z0-9_]{1,15}$/.test(username)) {
throw new Error(`Invalid Twitter username: ${username}`)
} Try / catch
try {
return await getUserProfile(username)
}
catch (err) {
if (/doesn't exist|suspended|User suspended/i.test((err as Error).message)) {
return null
}
throw err
} Prevention
- Validate the username format before scraping.
- Verify the session can view the target account (not blocked/protected).
- Detect 404/login pages right after goto to throw a more specific error.
When it happens
Trigger: The username does not exist (404 / 'This account doesn't exist' page) so [data-testid="UserName"] never appears and waitForSelector times out; the account is suspended or protected; the session is logged out and a login wall appears; a sub-selector for bio/avatar/follower count changed and the scrape throws.
Common situations: Looking up a renamed/deleted account; running without a valid session; Twitter changes the profile DOM; follower count element moved behind a click.
Related errors
- Failed to retweet: ${errorToMessage(error)}
- Failed to post tweet: ${errorToMessage(error)}
- Auth request failed (${error.status ?? 'unknown'})
- signOut failed
- Tweet element not found
AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12).
Data as JSON: /api/errors/1e76c98200b4e09c.
Report an issue: GitHub.