yikart/AiToEarn · error · BadRequestException
交换授权码失败: ${error.response?.data?.error_description || error.
Error message
交换授权码失败: ${error.response?.data?.error_description || error.message} What it means
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.
Source
Thrown at project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.auth.service.ts:294
})
);
if (data.error) {
throw new BadRequestException(`交换令牌失败: ${data}`);
}
// return {
// access_token: data.access_token,
// refresh_token: data.refresh_token,
// expires_in: data.expires_in,
// token_type: data.token_type,
// scope: data.scope,
// open_id: data.open_id
// };
return data;
} catch (error) {
this.logger.error('交换TikTok授权码失败:', error);
throw new BadRequestException(`交换授权码失败: ${error.response?.data?.error_description || error.message}`);
}
}
/**
* 获取TikTok用户资料
* @param accessToken 访问令牌
* @param openId 用户开放ID
* @returns TikTok用户资料
*/
async getTikTokUserProfile(accessToken: string, openId: string): Promise<TikTokUser> {
try {
const { data } = await firstValueFrom(
this.httpService.get(`${this.apiBaseUrl}/v2/user/info/`, {
params: {
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',
// open_id: openId
},
headers: {View on GitHub (pinned to d3aa8bea5b)
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.
Example fix
// before
throw new BadRequestException(`交换授权码失败: ${error.response?.data?.error_description || error.message}`);
// after
const desc = error.response?.data?.error_description || error.message;
this.logger.error(`TikTok token endpoint failure (status=${error.response?.status}): ${desc}`);
throw new BadRequestException(`交换授权码失败: ${desc}`); Defensive patterns
Strategy: try-catch
Validate before calling
// pre-flight connectivity check at service startup
const res = await fetch('https://open.tiktokapis.com/v2/oauth/token/', { method: 'HEAD' }).catch(() => null);
if (!res) throw new Error('TikTok token endpoint unreachable from this host'); Type guard
function isAxiosLikeError(e: unknown): e is { response?: { status: number; data: { error_description?: string } }; code?: string; message: string } {
return typeof e === 'object' && e !== null && ('response' in e || 'code' in e);
} Try / catch
try {
await api.exchangeTikTokCode(code);
} catch (e) {
if (!(e as any).response) {
// transport-level failure: retry with backoff
await sleep(1000);
return retryExchange(code);
}
throw new Error(`交换授权码失败: ${(e as any).response?.data?.error_description ?? (e as Error).message}`);
} Prevention
- 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).
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- 授权TikTok账户失败: ${error.response?.data?.error_description || e
- 交换令牌失败: ${data}
- No response from Gemini
- ChannelAccessTokenFailed
- ChannelAccessTokenFailed
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/3490dc8f5751d60b.
Report an issue: GitHub.