yikart/AiToEarn · error · AppException
ChannelAuthRefreshTokenMissing
ChannelAuthRefreshTokenMissing
Error message
ResponseCode.ChannelAuthRefreshTokenMissing
What it means
BilibiliAuthProvider.refresh requires a refresh token to renew credentials. If input.refreshToken is falsy it throws AppException ChannelAuthRefreshTokenMissing, because the OAuth refresh endpoint cannot be called without it.
Source
Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/bilibili/bilibili-auth.provider.ts:46
const url = this.bilibiliService.generateAuthUrl(input.state)
return { url, state: input.state, redirectUri: this.config.redirectUri }
}
async exchangeCode(input: AuthCallbackInput): Promise<CredentialResult> {
const callback = parseOAuthCallback(input)
const result = await this.bilibiliService.exchangeCode(callback.code)
return {
accessToken: result.accessToken,
refreshToken: result.refreshToken,
expiresAt: result.expiresAt,
}
}
async refresh(input: RefreshCredentialInput): Promise<CredentialResult> {
if (!input.refreshToken) {
throw new AppException(ResponseCode.ChannelAuthRefreshTokenMissing)
}
const result = await this.bilibiliService.refreshAccessToken(input.refreshToken)
return {
accessToken: result.accessToken,
refreshToken: result.refreshToken,
expiresAt: result.expiresAt,
}
}
async revoke(input: RevokeCredentialInput): Promise<void> {
await this.bilibiliService.revokeToken(input.accessToken)
}
async getProfile(input: CredentialContext): Promise<PlatformAccountProfile> {
const userInfo = await this.bilibiliService.getUserInfo(input.accessToken)
View on GitHub (pinned to d3aa8bea5b)
Solutions
- Re-run the Bilibili OAuth authorization flow to obtain a fresh accessToken+refreshToken pair.
- Check the stored credential record and backfill refreshToken where missing.
- Before calling refresh, guard: if (!credential.refreshToken) trigger re-auth instead of refresh.
- Persist refreshToken at exchange time and never overwrite it with null on partial updates.
Example fix
// before
await authProvider.refresh({ accessToken }) // refreshToken undefined
// after
if (!credential.refreshToken) {
await startReauth(credential.userId)
} else {
await authProvider.refresh(credential)
} Defensive patterns
Strategy: validation
Validate before calling
if (!credential.refreshToken) {
await startReauthorization(credential.userId)
return
} Type guard
function hasRefreshToken(c: Credential): c is Credential & { refreshToken: string } {
return typeof c.refreshToken === 'string' && c.refreshToken.length > 0
} Try / catch
try {
await authProvider.refresh(credential)
} catch (e) {
if (e.code === 'ChannelAuthRefreshTokenMissing') {
await markCredentialNeedsReauth(credential.userId)
return notifyReauthRequired(credential.userId)
}
throw e
} Prevention
- Persist refreshToken immediately during OAuth exchange and never null it in partial updates
- Detect legacy credentials missing refreshToken and force re-auth proactively
- Monitor credential rows with empty refresh_token and alert
When it happens
Trigger: Credential refresh flow invoked (e.g. token expiry handler or getUserAccessToken -> refresh) with a stored credential that has accessToken but no refreshToken saved during initial authorization.
Common situations: Old credentials created before refreshToken persistence was added; account connected via a flow that stored only the access token; refreshToken cleared after a failed refresh (Bilibili invalidates it); DB row with null refresh_token.
Related errors
- ChannelAccessTokenFailed
- ChannelRefreshTokenFailed
- ChannelAuthRefreshTokenMissing
- ChannelAuthRefreshTokenMissing
- ChannelAuthRefreshTokenMissing
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/636b93ba797caeea.
Report an issue: GitHub.