yikart/AiToEarn · error · BadRequestException
刷新令牌后未能获取访问令牌
Error message
刷新令牌后未能获取访问令牌
What it means
A BadRequestException thrown by getUserAccessToken after refreshAccessToken succeeded but the re-read of tiktok:accessToken:{accountId} from Redis returned nothing or a record without access_token. Indicates the refresh flow did not persist the new access token to Redis as expected.
Source
Thrown at project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.auth.service.ts:520
accountId: accountId,
platform: TokenPlatform.TIKTOK
});
if (!accountTokenInfo || !accountTokenInfo.refreshToken) {
throw new BadRequestException('无效的账号或刷新令牌丢失');
}
// 刷新并获取新令牌
const refreshResult = await this.refreshAccessToken(
accountTokenInfo.userId,
accountTokenInfo.accountId,
accountTokenInfo.refreshToken
);
// 刷新后再次从Redis获取
const newToken = await this.redisService.get(`tiktok:accessToken:${accountId}`);
if (!newToken || !newToken.access_token) {
throw new BadRequestException('刷新令牌后未能获取访问令牌');
}
return newToken.access_token;
}
/**
* 检查用户是否已授权TikTok
* @param accountId 账号ID
* @returns 是否已授权
*/
async isAuthorized(accountId: string): Promise<boolean> {
try {
const accessToken = await this.getUserAccessToken(accountId);
return !!accessToken;
} catch (error) {
return false;
}
}View on GitHub (pinned to d3aa8bea5b)
Solutions
- Check refreshAccessToken: ensure the setKey(`tiktok:accessToken:${accountId}`) call actually runs and stores the response's access_token.
- Confirm both refresh and read hit the same Redis instance/DB and the key TTL exceeds TikTok's access token lifetime (~24h).
- Instead of re-reading Redis, have refreshAccessToken return the new access_token directly and use it.
- Inspect Redis memory/eviction config if keys disappear under load.
Example fix
// before
await this.refreshAccessToken(accountTokenInfo.userId, accountTokenInfo.accountId, accountTokenInfo.refreshToken);
const newToken = await this.redisService.get(`tiktok:accessToken:${accountId}`);
// after
const refreshed = await this.refreshAccessToken(accountTokenInfo.userId, accountTokenInfo.accountId, accountTokenInfo.refreshToken);
if (!refreshed?.access_token) {
throw new BadRequestException('刷新令牌后未能获取访问令牌');
}
return refreshed.access_token; Defensive patterns
Strategy: fallback
Validate before calling
// after refresh, verify the cache entry before trusting it
const cached = await redis.get(`tiktok:accessToken:${accountId}`);
if (!cached?.access_token) {
throw new Error('refresh did not persist access token to redis');
} Type guard
function isStoredAccessToken(v: unknown): v is { access_token: string; expires_in: number } {
return typeof v === 'object' && v !== null && typeof (v as any).access_token === 'string';
} Try / catch
try {
return await api.getTikTokAccessToken(accountId);
} catch (e) {
if (/刷新令牌后未能获取/.test((e as Error).message)) {
// fallback: force a direct refresh and read the returned token
const fresh = await api.forceRefreshTikTokToken(accountId);
return fresh.access_token;
}
throw e;
} Prevention
- Have refreshAccessToken return the token directly instead of round-tripping through Redis.
- Set Redis TTL to match TikTok's access token lifetime (~24h).
- Use one Redis instance/DB consistently for token storage.
- Avoid allkeys-lru eviction for token keys; use volatile-ttl or dedicated storage.
When it happens
Trigger: refreshAccessToken completed but its Redis setKey call failed or was skipped, a Redis eviction/TTL removed the key between refresh and read, or a race where another flow overwrote/deleted the key, leaving no access_token field.
Common situations: Redis TTL set shorter than the access token lifetime, shared Redis instance with an eviction policy (allkeys-lru) under memory pressure, multi-instance deployment where refresh wrote to a different Redis DB than the read, key name typo/namespace drift.
Related errors
- 无效的状态参数或状态已过期
- 状态数据不完整
- 获取用户信息失败: ${error.response?.data?.error?.message || error.me
- No response from Gemini
- No subtitle entries in response
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/8551ccbd4dc1c85c.
Report an issue: GitHub.