{"record":{"id":"d4b083f93d93774f","repo":"yikart/AiToEarn","slug":"twitter-error-response-data-error-e","errorCode":null,"errorMessage":"获取Twitter时间线失败: ${error.response?.data?.error || error.message}","messagePattern":"获取Twitter时间线失败: (.+?)","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"project/aitoearn-electron/server/src/modules/plat/twitter/twitter.service.ts","lineNumber":66,"sourceCode":"      };\n      console.log(\"请求参数:\", params);\n      // console.log(\"accessToken:\", accessToken);\n      const response = await lastValueFrom(\n        this.httpService.get(url, {\n          params,\n          headers: {\n            Authorization: `Bearer ${accessToken}`,\n          },\n        })\n      );\n      console.log(\"==============response============\");\n      console.log(response);\n      return response.data\n    } catch (error) {\n      console.error('完整错误对象:', JSON.stringify(error.response?.data || error.message));\n\n      this.logger.error(`获取Twitter时间线失败: ${error.message}`, error.stack);\n      throw new BadRequestException(`获取Twitter时间线失败: ${error.response?.data?.error || error.message}`);\n    }\n  }\n\n  /**\n   * 发布新推文\n   * @param userId 用户ID\n   * @param accountId Twitter账号ID\n   * @param text 推文内容\n   * @param mediaIds 媒体ID数组\n   * @returns 发布结果\n   */\n  async createTweet(accessToken: string, userId: string, accountId: string, text: string, mediaIds?: string[]) {\n    // 获取当前最大的 id\n    const maxRecord = await this.PubRecordModel.findOne().sort({ id: -1 });\n    const newId = maxRecord ? maxRecord.id + 1 : 1;\n\n    try {\n","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/plat/twitter/twitter.service.ts#L48-L84","documentation":"getUserTimeline catches any failure from the upstream Twitter API HTTP call and rethrows it as a Nest BadRequestException prefixed with '获取Twitter时间线失败:'. The message embeds error.response?.data?.error (Twitter's error payload) or the axios error message. Common upstream causes are an expired/revoked access token, rate-limit (429), or bad query params.","triggerScenarios":"Twitter API returns non-2xx when fetching the user timeline: OAuth1 access token invalid/expired, 401/403 permissions, 429 rate limit, or network failure — any axios rejection inside getUserTimeline.","commonSituations":"User reconnected the account so the stored token was revoked; app hit the 15-min window rate limit; Twitter API v2 tier lacks required access; transient network/DNS outage on the server.","solutions":["Inspect error.response.data.error in the message to get Twitter's specific error code; fix the root cause (usually re-auth the account to refresh the access token).","If 429, back off until the rate-limit window resets (respect x-rate-limit-reset headers).","If 401/403, re-run the Twitter OAuth flow for that accountId and store the new token.","Add retry with exponential backoff for transient network/5xx errors instead of surfacing immediately.","Note this endpoint wraps all upstream errors as 400 — check server logs for the real status."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"const token = await getStoredAccessToken(accountId);\nif (!token) await reauthenticate(accountId); // avoid calling with a dead token","typeGuard":"function isTwitterApiError(msg: string): boolean {\n  return typeof msg === 'string' && msg.startsWith('获取Twitter时间线失败:');\n}","tryCatchPattern":"try {\n  const timeline = await api.get('/twitter/timeline', { params: { accountId } });\n} catch (e) {\n  const msg = e.response?.data?.message || '';\n  if (msg.includes('获取Twitter时间线失败')) {\n    const detail = msg.split(': ').slice(1).join(': ');\n    if (/429|rate limit/i.test(detail)) await backoffThenRetry();\n    else if (/401|Unauthorized/i.test(detail)) await reauthenticate(accountId);\n  }\n}","preventionTips":["Proactively re-auth accounts whose tokens are near expiry.","Respect Twitter's 15-minute rate-limit windows; cache timelines.","Parse the embedded error.response.data.error from the message for the real cause.","Retry transient network/5xx failures with exponential backoff.","Monitor for tokens revoked by users revoking app access."],"tags":["twitter-api","oauth","http-client","rate-limit","axios"],"backgroundTag":"upstream-api-request-failed","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}