yikart/AiToEarn · error · BadRequestException

获取推文详情失败: ${error.response?.data?.error || error.message}

Error message

获取推文详情失败: ${error.response?.data?.error || error.message}

What it means

getTweetDetail calls GET /2/tweets/:tweetId with tweet.field/expansion query params using a Bearer token. Axios errors from Twitter API v2 are wrapped in a BadRequestException whose message prefers Twitter's error.response.data.error detail over error.message.

Source

Thrown at project/aitoearn-electron/server/src/modules/plat/twitter/twitter.service.ts:239

      const params = {
        'tweet.fields': 'created_at,public_metrics,text,source',
        'expansions': 'attachments.media_keys,author_id',
        'media.fields': 'url,preview_image_url,type'
      };

      const response = await lastValueFrom(
        this.httpService.get(url, {
          params,
          headers: {
            'Authorization': `Bearer ${accessToken}`
          }
        })
      );

      return response.data.data
    } catch (error) {
      this.logger.error(`获取推文详情失败: ${error.message}`, error.stack);
      throw new BadRequestException(`获取推文详情失败: ${error.response?.data?.error || error.message}`);
    }
  }

  /**
   * 获取推文的统计数据
   * @param userId 用户ID
   * @param accountId Twitter账号ID
   * @param tweetId 推文ID
   * @returns 推文统计数据
   */
  async getTweetMetrics(accessToken, userId: string, accountId: string, tweetId: string) {
    try {

      const url = `${TWITTER_API_V2_URL}/tweets/${tweetId}`;
      const params = {
        'tweet.fields': 'public_metrics,non_public_metrics,organic_metrics', // 注意:某些指标需要高级API访问权限
      };

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Refresh the user access token when the message contains a 401/Unauthorized detail.
  2. Keep tweet IDs as strings end-to-end to avoid precision loss.
  3. Check tweet existence first with GET /2/tweets/:id and handle 404 gracefully.
  4. Confirm the token's scopes (tweet.read) and that the account can view the tweet.
  5. Read the full error.response.data.errors array for Twitter's exact error title/parameters.

Example fix

// before
const detail = await twitterService.getTweetDetail(accessToken, userId, accountId, tweetId)
// after
if (!/^[0-9]+$/.test(String(tweetId))) throw new Error('invalid tweetId')
const detail = await twitterService.getTweetDetail(accessToken, userId, accountId, String(tweetId)).catch(e => null)
if (!detail) return null // tweet deleted or inaccessible
Defensive patterns

Strategy: try-catch

Validate before calling

if (!/^[0-9]+$/.test(String(tweetId))) throw new Error('tweetId must be a numeric string')

Type guard

const isAxiosLike = (e: unknown): e is { response?: { status?: number; data?: { error?: string } } } =>
  typeof e === 'object' && e !== null && 'response' in e

Try / catch

try {
  return await twitterService.getTweetDetail(accessToken, userId, accountId, tweetId)
} catch (e) {
  if (isAxiosLike(e) && e.response?.status === 404) return null
  if (isAxiosLike(e) && e.response?.status === 401) return retryWithFreshToken()
  throw e
}

Prevention

When it happens

Trigger: Requesting a nonexistent or deleted tweetId (404), expired token (401), tweet not visible to the authorizing account (403 protected/private tweet), or malformed tweetId.

Common situations: Deleted tweets being re-fetched for analytics; private/protected accounts whose tweets the app can't read; API tier lacking access; expired cached access token; id passed as a float losing precision in JavaScript.

Related errors


AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31). Data as JSON: /api/errors/b8b982f61bf2352f. Report an issue: GitHub.