{"record":{"id":"1cc4cb8cf8f89630","repo":"yikart/AiToEarn","slug":"error-response-data-error-error-mes-1cc4cb","errorCode":null,"errorMessage":"搜索推文失败: ${error.response?.data?.error || error.message}","messagePattern":"搜索推文失败: (.+?)","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"project/aitoearn-electron/server/src/modules/plat/twitter/twitter.service.ts","lineNumber":308,"sourceCode":"        'max_results': isNaN(validMaxResults) ? 10 : validMaxResults, // 如果是NaN则使用默认值10\n        'tweet.fields': 'created_at,public_metrics,text',\n        'expansions': 'attachments.media_keys',\n        'media.fields': 'url,preview_image_url,type'\n      };\n\n      const response = await lastValueFrom(\n        this.httpService.get(url, {\n          params,\n          headers: {\n            'Authorization': `Bearer ${accessToken}`\n          }\n        })\n      );\n\n      return response.data;\n    } catch (error) {\n      this.logger.error(`搜索推文失败: ${error.message}`, error.stack);\n      throw new BadRequestException(`搜索推文失败: ${error.response?.data?.error || error.message}`);\n    }\n  }\n\n  /**\n   * 对推文点赞\n   * @param accessToken 访问令牌\n   * @param userId 用户ID\n   * @param accountId Twitter账号ID\n   * @param tweetId 推文ID\n   * @returns 点赞结果\n   */\n  async likeTweet(accessToken: string, userId: string, accountId: string, tweetId: string) {\n    try {\n      // Twitter API V2 点赞端点\n      const url = `${TWITTER_API_V2_URL}/users/${accountId}/likes`;\n      const data = {\n        tweet_id: tweetId\n      };","sourceCodeStart":290,"sourceCodeEnd":326,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/plat/twitter/twitter.service.ts#L290-L326","documentation":"searchTweets queries the Twitter API v2 recent-search endpoint with a Bearer token and returns response.data. Axios-level failures are rethrown as BadRequestException with Twitter's error detail (error.response.data.error) or the Axios message.","triggerScenarios":"Malformed query operators in the search string (400 Invalid query, e.g. unbalanced quotes or invalid operator), expired token (401), using user-context token where app-only is required or vice versa (403), recent search only covering the last 7 days, or 429 rate limiting.","commonSituations":"Users typing raw hashtags/keywords with unsupported operators; free tier lacking recent search access entirely; hitting the very low free-tier rate limit; query longer than 512/1024 char limit.","solutions":["Validate/encode the search query: escape quotes, remove unsupported operators, respect the char limit.","Confirm your API project tier includes recent search (Essential/Pro); upgrade if 403 'client-not-enrolled' appears.","Refresh the access token on 401 errors.","Use an app-only Bearer token for recent search if user context is not required.","Back off and retry on 429 using the x-rate-limit-reset header."],"exampleFix":"// before\nconst q = `from: ${user} \"new post\"`\nconst res = await twitterService.searchTweets(accessToken, userId, q)\n// after\nconst q = `from:${userHandle} \"new post\"`.slice(0, 512)\nif (!q.trim()) throw new Error('empty query')\nconst res = await twitterService.searchTweets(accessToken, userId, q)","handlingStrategy":"validation","validationCode":"function isValidSearchQuery(q: string): boolean {\n  return typeof q === 'string' && q.trim().length > 0 && q.length <= 512 &&\n    (q.split('\"').length - 1) % 2 === 0 // balanced quotes\n}","typeGuard":"const hasValidQuery = (q: unknown): q is string =>\n  typeof q === 'string' && q.trim().length > 0 && q.length <= 512","tryCatchPattern":"if (!hasValidQuery(query)) throw new Error('invalid search query')\ntry {\n  return await twitterService.searchTweets(accessToken, userId, query)\n} catch (e) {\n  if (/invalid query|400/i.test(String(e.message))) throw new Error(`query rejected by Twitter: ${query}`)\n  if (/429/.test(String(e.message))) { await sleep(16000); return retry() }\n  throw e\n}","preventionTips":["Sanitize user-provided search strings (escape quotes, drop unsupported operators)","Use app-only Bearer token for recent search when user context isn't needed","Check API tier access to recent search before shipping the feature","Cache search results to reduce rate-limit pressure"],"tags":["twitter-api","http-4xx","rate-limit","nestjs"],"backgroundTag":"twitter-api-request-failed","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}