yikart/AiToEarn · error · BadRequestException

获取推文统计数据失败: ${error.response?.data?.error || error.message}

Error message

获取推文统计数据失败: ${error.response?.data?.error || error.message}

What it means

getTweetMetrics calls GET /2/tweets/:tweetId (with public_metrics fields) and returns response.data. Any failure from the Twitter API v2 call is wrapped in a BadRequestException, message taken from error.response.data.error or error.message.

Source

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

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

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

      return response.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 query 查询内容
   * @param maxResults 最大结果数
   * @returns 搜索结果
   */
  async searchTweets(accessToken, userId: string, accountId: string, query: string, maxResults: number = 10) {
    try {
      // 确保maxResults是一个有效的整数
      const validMaxResults = Number(maxResults);

      const url = `${TWITTER_API_V2_URL}/tweets/search/recent`;
      const params = {

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Refresh the user access token on 401 and retry once.
  2. Honor 429: back off until rate-limit reset before calling again.
  3. Skip tweets that return 404 (deleted) instead of failing the whole batch.
  4. Verify public_metrics is requested in tweet.fields and your API tier allows it.
  5. Log error.response.data.errors for Twitter's precise error code.

Example fix

// before
const metrics = await twitterService.getTweetMetrics(accessToken, userId, accountId, tweetId)
// after
try {
  const metrics = await twitterService.getTweetMetrics(accessToken, userId, accountId, tweetId)
} catch (e) {
  if (String(e.message).includes('429')) await sleep(15000)
  else if (/not found/i.test(e.message)) return null
  else throw e
}
Defensive patterns

Strategy: retry

Validate before calling

if (!accessToken) throw new Error('missing access token before getTweetMetrics')

Type guard

const isRateLimited = (e: unknown): boolean =>
  (e as any)?.response?.status === 429 || /429|rate limit/i.test(String((e as any)?.message))

Try / catch

const fetchMetrics = async (attempt = 0): Promise<any> => {
  try {
    return await twitterService.getTweetMetrics(accessToken, userId, accountId, tweetId)
  } catch (e) {
    if (isRateLimited(e) && attempt < 3) {
      await sleep(15000 * 2 ** attempt)
      return fetchMetrics(attempt + 1)
    }
    if (/not found/i.test(String(e.message))) return null
    throw e
  }
}

Prevention

When it happens

Trigger: Expired or revoked user token (401), tweet not found/deleted (404), missing metrics field params for the access tier, or Twitter rate limit (429) on the metrics endpoint.

Common situations: Batch metric jobs hitting 429 rate limits; tracking metrics of tweets deleted by the author; old code calling after token rotation; free-tier access without metrics fields.

Related errors


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