yikart/AiToEarn · error · BadRequestException

删除推文失败: ${error.response?.data?.error || error.message}

Error message

删除推文失败: ${error.response?.data?.error || error.message}

What it means

deleteTweet calls DELETE https://api.twitter.com/2/tweets/:tweetId with a Bearer user token. Any Axios error from Twitter API v2 (HTTP 4xx/5xx) is caught and rethrown as a NestJS BadRequestException with the message taken from error.response.data.error (the Twitter error detail) or error.message. It is a wrapped upstream Twitter API failure, not a local validation error.

Source

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

  async deleteTweet(accessToken, userId: string, accountId: string, tweetId: string) {
    try {

      const url = `${TWITTER_API_V2_URL}/tweets/${tweetId}`;

      await lastValueFrom(
        this.httpService.delete(url, {
          headers: {
            'Authorization': `Bearer ${accessToken}`
          }
        })
      );

      this.logger.log(`成功删除推文: tweetId=${tweetId}, accountId=${accountId}`);

      return true
    } 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 getTweetDetail(accessToken, userId: string, accountId: string, tweetId: string) {
    try {

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

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Refresh the user's Twitter access token via TwitterAuthService before retrying (401 means expired).
  2. Verify the tweetId exists and belongs to accountId by calling GET /2/tweets/:id first.
  3. Ensure the OAuth2 token was issued with tweet.delete (tweet.write) scope and is a user-context token, not app-only Bearer.
  4. Inspect error.response.data (Twitter returns details/errors arrays) in the logged stack for the exact reason.
  5. Retry with exponential backoff only for 429/5xx; treat 403/404 as permanent.

Example fix

// before
await twitterService.deleteTweet(accessToken, userId, accountId, tweetId)
// after
try {
  const freshToken = await twitterAuthService.ensureFreshToken(accountId)
  await twitterService.deleteTweet(freshToken, userId, accountId, String(tweetId))
} catch (e) {
  if (e.getStatus?.() === 400 && /not found/i.test(e.message)) return // already deleted
  throw e
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!accessToken || !/^[0-9]+$/.test(String(tweetId))) throw new Error('invalid deleteTweet arguments')

Type guard

function hasTwitterErrorDetail(e: unknown): e is { response: { data: { error: string } } } {
  return typeof e === 'object' && e !== null && 'response' in e &&
    typeof (e as any).response?.data?.error === 'string'
}

Try / catch

try {
  await twitterService.deleteTweet(accessToken, userId, accountId, tweetId)
} catch (e) {
  const detail = e?.response?.data?.error || e.message
  if (/401|unauthorized/i.test(detail)) await refreshTokenAndRetry()
  else if (/not found/i.test(detail)) return // treat as deleted
  else throw new Error(`删除推文失败: ${detail}`)
}

Prevention

When it happens

Trigger: Calling deleteTweet with an expired/revoked OAuth2 user access token (401), a tweetId that does not exist or was already deleted (404 'Not found Tweet'), or an account whose app lacks tweet.delete scope / does not own the tweet (403 'Client not allowed to access this resource' or authorization error).

Common situations: Stale tokens after user re-authorizes the app; deleting a tweet that was already removed manually; using an app-only Bearer token instead of the user context token; incorrect tweetId passed from a frontend (e.g. truncated or non-numeric id); free-tier API access lacking write endpoints.

Related errors


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