yikart/AiToEarn · error · HttpException
授权TikTok账户失败: ${error.response?.data?.error_description || e
Error message
授权TikTok账户失败: ${error.response?.data?.error_description || error.message} What it means
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.
Source
Thrown at project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.auth.service.ts:243
tokenResponse.expires_in - 300 // 令牌过期前5分钟
);
// 生成系统令牌
const userInfo = await this.userModel.findOne({ _id: userId });
const systemTokenInfo = {
phone: userInfo?.phone ?? '',
id: userId,
name: userInfo.name,
isManager: false,
googleId: userInfo?.googleAccount?.googleId ?? ''
};
const systemToken = await this.authService.generateToken(systemTokenInfo);
return { data: systemTokenInfo };
} catch (error) {
this.logger.error('处理TikTok授权回调失败:', error);
throw new HttpException(
'授权TikTok账户失败: ' + (error.response?.data?.error_description || error.message),
error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR
);
}
}
/**
* 交换授权码获取令牌
* @param code 授权码
* @returns TikTok OAuth令牌响应
*/
private async exchangeCodeForTokens(code: string, codeVerifier: string): Promise<TikTokOAuthTokenResponse> {
try {
// 构建请求体
const params = new URLSearchParams({
client_key: this.clientId,
client_secret: this.clientSecret,
code: code,View on GitHub (pinned to d3aa8bea5b)
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.
Example fix
// before
catch (error) {
throw new HttpException('授权TikTok账户失败: ' + (error.response?.data?.error_description || error.message), ...);
}
// after — fail fast with a specific message before the outer wrap
if (!code || !state) {
throw new BadRequestException('code and state are required');
}
const stateData = await this.redisService.get(`tiktok:state:${state}`);
if (!stateData) {
throw new BadRequestException('授权状态已过期,请重新发起授权');
} Defensive patterns
Strategy: try-catch
Validate before calling
// client-side, before hitting the callback flow
const hasCode = typeof code === 'string' && code.length > 0;
const hasState = typeof state === 'string' && state.length > 0;
if (!hasCode || !hasState) throw new Error('OAuth callback requires code and state'); Type guard
function hasTikTokErrorData(e: unknown): e is { response: { status: number; data: { error_description: string } } } {
return typeof e === 'object' && e !== null && 'response' in e &&
typeof (e as any).response?.data?.error_description === 'string';
} Try / catch
try {
await api.handleTikTokCallback(code, state);
} catch (e) {
const desc = (e as any).response?.data?.error_description ?? (e as Error).message;
logger.warn(`TikTok auth failed: ${desc}`);
if (/state|expired/i.test(desc)) redirectUserToReauth();
else showUserError('授权失败,请重试');
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- 交换令牌失败: ${data}
- 交换授权码失败: ${error.response?.data?.error_description || error.
- No response from Gemini
- ChannelAccessTokenFailed
- ChannelAccessTokenFailed
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/d2c7375313024579.
Report an issue: GitHub.