{"record":{"id":"1e76c98200b4e09c","repo":"moeru-ai/airi","slug":"failed-to-fetch-profile-for-username","errorCode":null,"errorMessage":"Failed to fetch profile for @${username}","messagePattern":"Failed to fetch profile for @(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"integrations/twitter-services/src/core/services/user.ts","lineNumber":89,"sourceCode":"        ? Number.parseInt((await followElement.textContent() || '0').replace(/\\D/g, ''))\n        : undefined\n\n      const followingCount = followingElement\n        ? Number.parseInt((await followingElement.textContent() || '0').replace(/\\D/g, ''))\n        : undefined\n\n      return {\n        username,\n        displayName,\n        bio: bio || undefined,\n        avatarUrl: avatarUrl || undefined,\n        followersCount: followerCount || undefined,\n        followingCount: followingCount || undefined,\n      }\n    }\n    catch (error) {\n      logger.main.error('Error fetching user profile:', (error as Error).message)\n      throw new Error(`Failed to fetch profile for @${username}`)\n    }\n  }\n\n  return {\n    followUser,\n    getUserProfile,\n  }\n}\n","sourceCodeStart":71,"sourceCodeEnd":98,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/integrations/twitter-services/src/core/services/user.ts#L71-L98","documentation":"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.","triggerScenarios":"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.","commonSituations":"Looking up a renamed/deleted account; running without a valid session; Twitter changes the profile DOM; follower count element moved behind a click.","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."],"exampleFix":"// before\ncatch (error) {\n  logger.main.error('Error fetching user profile:', (error as Error).message)\n  throw new Error(`Failed to fetch profile for @${username}`)\n}\n\n// after\ncatch (error) {\n  const cause = (error as Error).message\n  logger.main.error('Error fetching user profile:', cause)\n  throw new Error(`Failed to fetch profile for @${username}: ${cause}`)\n}","handlingStrategy":"try-catch","validationCode":"if (!/^[A-Za-z0-9_]{1,15}$/.test(username)) {\n  throw new Error(`Invalid Twitter username: ${username}`)\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await getUserProfile(username)\n}\ncatch (err) {\n  if (/doesn't exist|suspended|User suspended/i.test((err as Error).message)) {\n    return null\n  }\n  throw err\n}","preventionTips":["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."],"tags":["browser-automation","twitter","profile","dom-selector","session"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}