yikart/AiToEarn · critical · AppException
ChannelAccessTokenFailed
ChannelAccessTokenFailed
Error message
ChannelAccessTokenFailed
What it means
ChannelAccessTokenFailed is thrown by DouyinService.getClientToken when POST /oauth/client_token/ does not return data.access_token. This token (client_credentials grant using client_key/client_secret) is required for all app-level Douyin API calls (share-id, ticket). The error means the credential exchange itself failed, typically because the platform returned an error envelope.
Source
Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/douyin/douyin.service.ts:370
}
return cached.access_token
}
}
const response = await this.http.post<DouyinOAuthEnvelope<DouyinClientTokenResponse>>(
'/oauth/client_token/',
{
grant_type: DouyinOAuthGrantType.ClientCredential,
client_key: this.cfg.clientId,
client_secret: this.cfg.clientSecret,
},
{
headers: { 'Content-Type': 'application/json' },
},
)
const result = response.data.data
if (!result?.access_token) {
throw new AppException(ResponseCode.ChannelAccessTokenFailed, { platform: AccountType.Douyin, field: 'access_token', reasonCode: 'missing_platform_field' })
}
const expiresIn = Number(result.expires_in)
const expiresAt = now + expiresIn * 1000
this.clientTokenCache = {
accessToken: result.access_token,
expiresAt,
}
await this.redis.saveDouyinClientToken({
access_token: result.access_token,
expires_in: expiresIn,
expiresAt,
})
return result.access_token
}
private async getOpenTicket(): Promise<string> {
const clientToken = await this.getClientToken()View on GitHub (pinned to d3aa8bea5b)
Solutions
- Check the response body of /oauth/client_token/ for the platform error_code (e.g. 40015 invalid client_secret) and correct DOUYIN_CLIENT_KEY/DOUYIN_CLIENT_SECRET accordingly.
- Confirm the Douyin Open Platform app is approved and not suspended, and that the client_key matches an app with the required scopes.
- Clear the stale Redis douyin client-token cache key if it holds an expired/rejected token, then retry.
- If the secret was rotated, redeploy servers with the new secret before the old one is revoked.
Example fix
// before DOUYIN_CLIENT_KEY=aw9x0000000000 DOUYIN_CLIENT_SECRET= // empty -> /oauth/client_token/ fails -> throws // after DOUYIN_CLIENT_KEY=<real client_key> DOUYIN_CLIENT_SECRET=<matching client_secret> // response now has data.access_token
Defensive patterns
Strategy: validation
Validate before calling
function assertDouyinCredentials(cfg: { clientId?: string; clientSecret?: string }) {
if (!cfg.clientId || !cfg.clientSecret) throw new Error('DOUYIN_CLIENT_KEY/DOUYIN_CLIENT_SECRET are required')
}
assertDouyinCredentials(douyinConfig) Type guard
const hasClientToken = (r: unknown): r is { data: { data: { access_token: string; expires_in: number } } } =>
!!r && typeof r === 'object' && typeof (r as any).data?.data?.access_token === 'string' Try / catch
try {
const token = await douyinService.clientToken()
} catch (err) {
if (err instanceof AppException && err.code === ResponseCode.ChannelAccessTokenFailed) {
// reload config, clear Redis token cache, alert ops about credential failure
} else throw err
} Prevention
- Validate client_key/client_secret are set and non-empty at service startup
- Rotate platform secrets with zero downtime: deploy new secret before revoking the old one
- Monitor token fetch failure rate; a spike means credentials or the platform changed
- Keep the Redis token cache TTL aligned with expires_in minus a safety buffer
When it happens
Trigger: Any first call or cache-miss call to getClientToken (directly or via clientToken/refreshedClientToken after cache invalidation) where Douyin's response lacks data.access_token — e.g. wrong client_key, invalid client_secret, app suspended, or platform error payload.
Common situations: DOUYIN_CLIENT_KEY/DOUYIN_CLIENT_SECRET env vars missing, swapped, or rotated on the platform but not on the server; both in-memory and Redis token caches empty on a fresh deployment; Douyin app disabled or under review; clock/network issues producing a malformed response.
Related errors
- ChannelAuthRefreshTokenMissing
- ChannelAuthPlatformUidMissing
- ChannelAccessTokenFailed
- ChannelAuthorizationFailed
- ChannelAuthCsrfInvalid
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/d582ed9ecdd2fb65.
Report an issue: GitHub.