{"record":{"id":"d675a6c3c520cae8","repo":"moeru-ai/airi","slug":"failed-to-get-tweet-details-errortomessage-erro","errorCode":null,"errorMessage":"Failed to get tweet details: ${errorToMessage(error)}","messagePattern":"Failed to get tweet details: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"integrations/twitter-services/src/core/services/tweet.ts","lineNumber":326,"sourceCode":"      for (const replyElement of replyElements) {\n        const extractedReply = await TweetParser.extractTweetData(page, replyElement)\n        if (extractedReply) {\n          replies.push(extractedReply)\n        }\n      }\n\n      // Construct the detailed tweet\n      const tweetDetail: TweetDetail = {\n        ...mainTweet,\n        replies: replies.length > 0 ? replies : undefined,\n        quotedTweet,\n      }\n\n      return tweetDetail\n    }\n    catch (error: unknown) {\n      console.error('Error getting tweet details:', error)\n      throw new Error(`Failed to get tweet details: ${errorToMessage(error)}`)\n    }\n  }\n\n  return {\n    searchTweets,\n    likeTweet,\n    retweet,\n    postTweet,\n    getTweetDetails,\n  }\n}\n","sourceCodeStart":308,"sourceCodeEnd":338,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/integrations/twitter-services/src/core/services/tweet.ts#L308-L338","documentation":"This is the outer catch for getTweetDetails that re-throws any error raised during navigation, element selection, parsing, or reply scraping as a single normalized message via errorToMessage. It is a pass-through wrapper; the real cause is one of the inner throws (182, 183) or a Puppeteer navigation/timeout error.","triggerScenarios":"Any uncaught error from page.goto, waitForSelector, TweetParser.extractTweetData, reply extraction, or the inner 'Tweet element not found'/'Failed to extract tweet data' throws propagates here and is rewrapped.","commonSituations":"Network/proxy failure during page.goto; the inner errors 182/183 bubble up; reply selector changes cause the reply loop to throw; long thread where reply scraping times out.","solutions":["Inspect the console.error output (already logged) to see whether the cause is navigation, element-not-found, or parser failure, then address that specific inner error.","Wrap the reply-scraping loop so a single reply failure does not abort the whole getTweetDetails call.","Validate the tweet id and session before calling, and consider a single retry on transient navigation errors."],"exampleFix":"// before\ntry {\n  // ... replies = await scrapeReplies(page)\n  return tweetDetail\n}\ncatch (error: unknown) {\n  throw new Error(`Failed to get tweet details: ${errorToMessage(error)}`)\n}\n\n// after\nlet replies: Tweet[] = []\ntry {\n  replies = await scrapeReplies(page)\n}\ncatch (replyError) {\n  console.warn('Reply scraping failed; returning tweet without replies:', replyError)\n}\nreturn { ...mainTweet, replies: replies.length > 0 ? replies : undefined, quotedTweet }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  return await getTweetDetails(tweetId)\n}\ncatch (err) {\n  // distinguish transient navigation errors from permanent ones\n  if (/net::ERR|Navigation timeout/i.test((err as Error).message)) {\n    return getTweetDetails(tweetId) // single retry\n  }\n  throw err\n}","preventionTips":["Wrap the reply-scraping sub-step so a single reply failure does not abort the whole call.","Log the underlying cause alongside the wrapped message.","Validate tweet id and session before invoking."],"tags":["browser-automation","twitter","wrapper","propagation"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}