{"record":{"id":"f80d5546a999a4c4","repo":"yikart/AiToEarn","slug":"err-response-data-error-description","errorCode":null,"errorMessage":"${err.response?.data?.error_description || '刷新令牌失败'}","messagePattern":"\\$\\{err\\.response\\?\\.data\\?\\.error_description \\|\\| '刷新令牌失败'\\}","errorType":"exception","errorClass":"HttpException","httpStatus":400,"severity":"error","filePath":"project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.auth.service.ts","lineNumber":478,"sourceCode":"        );\n      }\n\n      this.logger.log('刷新TikTok访问令牌成功');\n      // 返回系统令牌用于前端重定向\n      const userInfo = await this.userModel.findOne({_id: userId});\n      const systemTokenInfo = {\n        phone: userInfo?.phone ?? '',\n        id: userId,\n        name: userInfo.name,\n        isManager: false,\n        googleId: userInfo?.googleAccount?.googleId ?? ''\n      };\n\n      const systemToken = await this.authService.generateToken(systemTokenInfo);\n      return { url: systemToken };\n    } catch (err) {\n      this.logger.error('刷新TikTok访问令牌失败:', err.response?.data || err.message);\n      throw new HttpException(\n        err.response?.data?.error_description || '刷新令牌失败',\n        err.response?.status || HttpStatus.BAD_REQUEST\n      );\n    }\n  }\n\n  /**\n   * 获取用户的TikTok访问令牌\n   * @param accountId 账号ID\n   * @returns 访问令牌\n   */\n  async getUserAccessToken(accountId: string): Promise<string> {\n    this.logger.log(`获取TikTok访问令牌: accountId=${accountId}`);\n\n    // 先检查Redis缓存\n    const cachedToken = await this.redisService.get(`tiktok:accessToken:${accountId}`);\n    if (cachedToken && cachedToken.access_token) {\n      this.logger.log(\"从Redis获取到有效令牌\");","sourceCodeStart":460,"sourceCodeEnd":496,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.auth.service.ts#L460-L496","documentation":"An HttpException thrown by refreshAccessToken's outer catch. When the refresh POST itself throws (network failure or non-200 status), it surfaces the upstream error_description and propagates the upstream status code, defaulting to 400. The fallback literal is the generic '刷新令牌失败'.","triggerScenarios":"The axios POST to TikTok's oauth/token/ endpoint rejects: connectivity failure, timeout, TLS error, or TikTok returned 400/401 (bad client credentials, malformed body) — i.e. cases where no { error } body was parsed into data.","commonSituations":"Egress firewall blocking open.tiktokapis.com, DNS issues, request timeout under load, client secret rotated on the TikTok dashboard while the server still has the old value.","solutions":["Check the logged err.response?.data for TikTok's error payload; fix the named parameter.","Verify network connectivity/proxy configuration from the server to open.tiktokapis.com.","Re-sync TIKTOK_CLIENT_KEY/TIKTOK_CLIENT_SECRET with the current values in the TikTok Developers console.","Implement a bounded retry with backoff for transient network/5xx failures before surfacing the error."],"exampleFix":"// before\nthrow new HttpException(\n  err.response?.data?.error_description || '刷新令牌失败',\n  err.response?.status || HttpStatus.BAD_REQUEST\n);\n// after\nconst retryable = !err.response || err.response.status >= 500 || err.code === 'ECONNABORTED';\nif (retryable) {\n  throw new HttpException('刷新令牌失败（网络临时故障，请重试）', HttpStatus.BAD_GATEWAY);\n}\nthrow new HttpException(err.response?.data?.error_description || '刷新令牌失败', err.response?.status || HttpStatus.BAD_REQUEST);","handlingStrategy":"retry","validationCode":"async function canReachTikTok(): Promise<boolean> {\n  try { await axios.head('https://open.tiktokapis.com', { timeout: 5000 }); return true; }\n  catch { return false; }\n}","typeGuard":"function isNetworkError(e: unknown): boolean {\n  const anyE = e as any;\n  return !anyE?.response || ['ECONNABORTED', 'ECONNREFUSED', 'ENOTFOUND', 'ETIMEDOUT'].includes(anyE.code);\n}","tryCatchPattern":"try {\n  return await withRetry(() => api.refreshTikTokToken(token), { retries: 2, backoffMs: 1000 });\n} catch (e) {\n  if (isNetworkError(e)) throw new ServiceUnavailableError('TikTok token endpoint unreachable');\n  throw e;\n}","preventionTips":["Set explicit axios timeouts and retry only transport/5xx errors.","Alert on failed egress to open.tiktokapis.com.","Keep client credentials in validated env config checked at boot.","Never treat a network error as an OAuth rejection."],"tags":["tiktok","refresh-token","network","http-exception"],"backgroundTag":"oauth-token-endpoint-unreachable","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}