{"record":{"id":"d2c7375313024579","repo":"yikart/AiToEarn","slug":"tiktok-error-response-data-error-descri","errorCode":null,"errorMessage":"授权TikTok账户失败: ${error.response?.data?.error_description || error.message}","messagePattern":"授权TikTok账户失败: (.+?)","errorType":"exception","errorClass":"HttpException","httpStatus":500,"severity":"error","filePath":"project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.auth.service.ts","lineNumber":243,"sourceCode":"        tokenResponse.expires_in - 300 // 令牌过期前5分钟\n      );\n\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\n      return { data: systemTokenInfo };\n    } catch (error) {\n      this.logger.error('处理TikTok授权回调失败:', error);\n      throw new HttpException(\n        '授权TikTok账户失败: ' + (error.response?.data?.error_description || error.message),\n        error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR\n      );\n    }\n  }\n\n  /**\n   * 交换授权码获取令牌\n   * @param code 授权码\n   * @returns TikTok OAuth令牌响应\n   */\n  private async exchangeCodeForTokens(code: string, codeVerifier: string): Promise<TikTokOAuthTokenResponse> {\n    try {\n      // 构建请求体\n      const params = new URLSearchParams({\n        client_key: this.clientId,\n        client_secret: this.clientSecret,\n        code: code,","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.auth.service.ts#L225-L261","documentation":"An HttpException thrown by handleAuthorizationCallback when any step of processing TikTok's OAuth callback fails (token exchange, profile fetch, saving state/token). It wraps the original error, preferring TikTok's OAuth error_description from the HTTP response body, falling back to error.message. The status is propagated from the upstream response if present, otherwise 500.","triggerScenarios":"TikTok redirects the user back to /tiktok/callback with a code and state, and handleAuthorizationCallback fails while calling exchangeCodeForTokens or getTikTokUserProfile — e.g. invalid/expired auth code, state not found in Redis, TikTok returning 4xx/5xx from the token or user endpoints.","commonSituations":"Misconfigured TIKTOK_CLIENT_KEY/CLIENT_SECRET env vars, user denying the consent screen, replaying an already-used code, state expired in Redis (short TTL), TikTok sandbox app vs live app mismatch.","solutions":["Read error_description in the thrown message — it names the exact TikTok OAuth failure (e.g. invalid_request, invalid_grant) and fix that parameter.","Verify TIKTOK_CLIENT_KEY, TIKTOK_CLIENT_SECRET and redirect_uri exactly match the TikTok Developers console app settings.","Check that the state passed to the callback still exists in Redis before the code expires; retry the whole OAuth flow if it doesn't.","Confirm the authorization code is used once and immediately — TikTok codes are single-use and short-lived."],"exampleFix":"// before\ncatch (error) {\n  throw new HttpException('授权TikTok账户失败: ' + (error.response?.data?.error_description || error.message), ...);\n}\n// after — fail fast with a specific message before the outer wrap\nif (!code || !state) {\n  throw new BadRequestException('code and state are required');\n}\nconst stateData = await this.redisService.get(`tiktok:state:${state}`);\nif (!stateData) {\n  throw new BadRequestException('授权状态已过期，请重新发起授权');\n}","handlingStrategy":"try-catch","validationCode":"// client-side, before hitting the callback flow\nconst hasCode = typeof code === 'string' && code.length > 0;\nconst hasState = typeof state === 'string' && state.length > 0;\nif (!hasCode || !hasState) throw new Error('OAuth callback requires code and state');","typeGuard":"function hasTikTokErrorData(e: unknown): e is { response: { status: number; data: { error_description: string } } } {\n  return typeof e === 'object' && e !== null && 'response' in e &&\n    typeof (e as any).response?.data?.error_description === 'string';\n}","tryCatchPattern":"try {\n  await api.handleTikTokCallback(code, state);\n} catch (e) {\n  const desc = (e as any).response?.data?.error_description ?? (e as Error).message;\n  logger.warn(`TikTok auth failed: ${desc}`);\n  if (/state|expired/i.test(desc)) redirectUserToReauth();\n  else showUserError('授权失败，请重试');\n}","preventionTips":["Always start the flow via the auth-url endpoint so state is stored in Redis with adequate TTL.","Never reuse or bookmark callback URLs — codes are single-use.","Log the wrapped error_description server-side to diagnose TikTok-side rejections quickly.","Keep client_key/secret/redirect_uri in one validated config module."],"tags":["oauth","tiktok","http-exception","token-exchange"],"backgroundTag":"oauth-callback-failed","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}