yikart/AiToEarn · error · AppException
ChannelAuthRefreshTokenMissing
ChannelAuthRefreshTokenMissing
Error message
ChannelAuthRefreshTokenMissing
What it means
ChannelAuthRefreshTokenMissing is thrown by the LinkedIn auth provider's refresh() when the credential being refreshed has no refreshToken. OAuth refresh requires a refresh token; without one the provider refuses to call LinkedIn's token endpoint. This is a guard against an unrecoverable credential state.
Source
Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/linkedin/linkedin-auth.provider.ts:46
return { url, state: input.state, redirectUri: this.config.redirectUri }
}
async exchangeCode(input: AuthCallbackInput): Promise<CredentialResult> {
const callback = parseOAuthCallback(input)
const result = await this.linkedinService.exchangeCode(callback.code)
return {
accessToken: result.accessToken,
refreshToken: result.refreshToken,
expiresAt: result.expiresAt,
scope: result.scope,
}
}
async refresh(input: RefreshCredentialInput): Promise<CredentialResult> {
if (!input.refreshToken) {
throw new AppException(ResponseCode.ChannelAuthRefreshTokenMissing)
}
const result = await this.linkedinService.refreshAccessToken(input.refreshToken)
return {
accessToken: result.accessToken,
refreshToken: result.refreshToken,
expiresAt: result.expiresAt,
scope: result.scope,
}
}
async revoke(input: RevokeCredentialInput): Promise<void> {
await this.linkedinService.revokeToken(input.accessToken)
}
async getProfile(input: CredentialContext): Promise<PlatformAccountProfile> {
const profile = await this.linkedinService.getProfile(input.accessToken)View on GitHub (pinned to d3aa8bea5b)
Solutions
- Ensure the LinkedIn OAuth authorization flow requests offline_access and stores the returned refresh token
- Re-authorize the channel (re-run the connect/OAuth flow) to obtain a fresh refresh token
- Check the credential storage layer for rows missing refresh_token and backfill via re-auth
Example fix
// before
await linkedinAuthProvider.refresh({ accessToken }) // throws ChannelAuthRefreshTokenMissing
// after
if (!credential.refreshToken) await reconnectChannel(credential.channelId)
await linkedinAuthProvider.refresh({ accessToken, refreshToken: credential.refreshToken }) Defensive patterns
Strategy: validation
Validate before calling
if (!credential.refreshToken) {
throw new Error('LinkedIn credential missing refresh token; reconnect the channel')
}
await linkedinAuthProvider.refresh({ accessToken: credential.accessToken, refreshToken: credential.refreshToken }) Type guard
function hasRefreshToken(c: { refreshToken?: string | null }): c is { refreshToken: string } {
return typeof c.refreshToken === 'string' && c.refreshToken.length > 0
} Try / catch
try {
await linkedinAuthProvider.refresh(input)
} catch (e) {
if (e instanceof AppException && e.code === ResponseCode.ChannelAuthRefreshTokenMissing) {
await markChannelNeedsReauth(channelId)
} else throw e
} Prevention
- Always request offline_access scope in the LinkedIn OAuth flow
- Persist refresh_token immediately at connect time; never store access-token-only rows
- Monitor for credentials with null refresh_token and trigger re-auth proactively
- Re-auth channels after user revocation instead of calling refresh
When it happens
Trigger: Calling refresh() with input.refreshToken undefined/empty — typically a credential created via a flow that returned only an accessToken, or a stored credential where the refresh token was never persisted or was cleared.
Common situations: LinkedIn OAuth apps where the user reconnected without offline_access scope so no refresh token was issued; DB rows seeded manually with only an access token; refresh tokens deleted after revocation by the user.
Related errors
- ChannelAuthRefreshTokenMissing
- ChannelAuthRefreshTokenMissing
- ChannelAccessTokenFailed
- ChannelRefreshTokenFailed
- ChannelAuthRefreshTokenMissing
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/9209dffe3dc391b2.
Report an issue: GitHub.