{"record":{"id":"3490dc8f5751d60b","repo":"yikart/AiToEarn","slug":"error-response-data-error-description","errorCode":null,"errorMessage":"交换授权码失败: ${error.response?.data?.error_description || error.message}","messagePattern":"交换授权码失败: (.+?)","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.auth.service.ts","lineNumber":294,"sourceCode":"        })\n      );\n\n      if (data.error) {\n        throw new BadRequestException(`交换令牌失败: ${data}`);\n      }\n\n      // return {\n      //   access_token: data.access_token,\n      //   refresh_token: data.refresh_token,\n      //   expires_in: data.expires_in,\n      //   token_type: data.token_type,\n      //   scope: data.scope,\n      //   open_id: data.open_id\n      // };\n      return data;\n    } catch (error) {\n      this.logger.error('交换TikTok授权码失败:', error);\n      throw new BadRequestException(`交换授权码失败: ${error.response?.data?.error_description || error.message}`);\n    }\n  }\n\n  /**\n   * 获取TikTok用户资料\n   * @param accessToken 访问令牌\n   * @param openId 用户开放ID\n   * @returns TikTok用户资料\n   */\n  async getTikTokUserProfile(accessToken: string, openId: string): Promise<TikTokUser> {\n    try {\n      const { data } = await firstValueFrom(\n        this.httpService.get(`${this.apiBaseUrl}/v2/user/info/`, {\n          params: {\n            fields: 'open_id,union_id,avatar_url,bio_description,profile_deep_link,is_verified,follower_count,following_count,likes_count,video_count,username, display_name',\n            // open_id: openId\n          },\n          headers: {","sourceCodeStart":276,"sourceCodeEnd":312,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.auth.service.ts#L276-L312","documentation":"A BadRequestException thrown by exchangeCodeForTokens' outer catch. When the axios call to TikTok's token endpoint throws (network error or non-2xx status), the handler prefers TikTok's error_description from the response body, falling back to error.message, and wraps it in a Chinese-language message.","triggerScenarios":"The POST to TikTok's oauth/token endpoint fails at the transport level (DNS, timeout, TLS) or returns 400/401/403 (bad client credentials, invalid code, mismatched redirect_uri), and the axios promise rejects instead of returning a body with error.","commonSituations":"Network egress blocked from the server to open.tiktokapis.com, expired/rotated client secret, user refreshed the callback page causing code reuse, missing client_key parameter in the form body.","solutions":["Read error_description in the message for TikTok's own explanation; fix the named parameter.","Confirm outbound HTTPS connectivity to open.tiktokapis.com from the server (proxy/firewall rules).","Re-check client_key, client_secret, code, grant_type=authorization_code and redirect_uri in the request body.","Restart the OAuth flow to get a fresh single-use code."],"exampleFix":"// before\nthrow new BadRequestException(`交换授权码失败: ${error.response?.data?.error_description || error.message}`);\n// after\nconst desc = error.response?.data?.error_description || error.message;\nthis.logger.error(`TikTok token endpoint failure (status=${error.response?.status}): ${desc}`);\nthrow new BadRequestException(`交换授权码失败: ${desc}`);","handlingStrategy":"try-catch","validationCode":"// pre-flight connectivity check at service startup\nconst res = await fetch('https://open.tiktokapis.com/v2/oauth/token/', { method: 'HEAD' }).catch(() => null);\nif (!res) throw new Error('TikTok token endpoint unreachable from this host');","typeGuard":"function isAxiosLikeError(e: unknown): e is { response?: { status: number; data: { error_description?: string } }; code?: string; message: string } {\n  return typeof e === 'object' && e !== null && ('response' in e || 'code' in e);\n}","tryCatchPattern":"try {\n  await api.exchangeTikTokCode(code);\n} catch (e) {\n  if (!(e as any).response) {\n    // transport-level failure: retry with backoff\n    await sleep(1000);\n    return retryExchange(code);\n  }\n  throw new Error(`交换授权码失败: ${(e as any).response?.data?.error_description ?? (e as Error).message}`);\n}","preventionTips":["Add a timeout to the axios call and a bounded retry for network errors.","Monitor egress to open.tiktokapis.com from the deployment environment.","Rotate client secrets via config management, not manual edits.","Distinguish transport errors (retryable) from OAuth rejections (not retryable)."],"tags":["oauth","tiktok","network","token-exchange"],"backgroundTag":"oauth-token-exchange-rejected","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}