yikart/AiToEarn · error · AppException
ResponseCode.ChannelAccountNotAuthorized
ResponseCode.ChannelAccountNotAuthorized
Error message
ChannelAccountNotAuthorized
What it means
getCredentialAccount throws ChannelAccountNotAuthorized (AppException, ResponseCode.ChannelAccountNotAuthorized) when the account exists and is not a Relay account but its status is AccountStatus.ABNORMAL. ABNORMAL status is set after non-retryable credential failures (e.g. markAccountOfflineForCredentialFailure), meaning the stored token is known-bad and the account must be re-authorized before use.
Source
Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/auth/auth.service.ts:629
accessToken: refreshed.accessToken,
refreshToken: refreshed.refreshToken,
expiresAt: refreshed.expiresAt,
scope: refreshed.scope,
}
}
private async getCredentialAccount(accountId: string, userId?: string) {
const account = userId
? await this.accountRepo.getByIdAndUserId(accountId, userId)
: await this.accountRepo.getAccountById(accountId)
if (!account) {
throw new AppException(ResponseCode.AccountNotFound)
}
if (account.relayAccountRef) {
throw new RelayAccountException(account.relayAccountRef, accountId)
}
if (account.status === AccountStatus.ABNORMAL) {
throw new AppException(ResponseCode.ChannelAccountNotAuthorized)
}
return account
}
async markAccountOfflineForCredentialFailure(
accountId: string,
error: unknown,
reason = 'platform_auth_failed',
): Promise<boolean> {
if (!this.isCredentialFailure(error)) {
return false
}
try {
await this.markAccountOffline(accountId, reason)
return true
}
catch (markError) {
this.logger.warn(error, `Credential failure for account ${accountId}`)View on GitHub (pinned to d3aa8bea5b)
Solutions
- Re-run the platform OAuth flow to reconnect the account; this resets status to NORMAL with fresh tokens.
- Check account.status before credential calls and short-circuit with a 'reconnect required' user prompt instead of invoking the service.
- Filter scheduled/batch jobs to exclude ABNORMAL accounts to avoid systematic failures.
- Investigate the original failure (logs of markAccountOfflineForCredentialFailure) to confirm why the account went ABNORMAL before reconnecting.
Example fix
// before
await authService.refreshCredential(accountId, userId)
// after
const account = await accountRepo.getAccountById(accountId)
if (account?.status === AccountStatus.ABNORMAL) {
throw new AppException(ResponseCode.ChannelAccountNotAuthorized, { accountId })
}
await authService.refreshCredential(accountId, userId) Defensive patterns
Strategy: validation
Validate before calling
const account = await accountRepo.getAccountById(accountId)
if (account?.status === AccountStatus.ABNORMAL) {
throw new AppException(ResponseCode.ChannelAccountNotAuthorized, { accountId })
} Type guard
function isUsableAccount(a: { status: AccountStatus } | null): a is { status: AccountStatus } {
return !!a && a.status === AccountStatus.NORMAL
} Try / catch
try {
await authService.refreshCredential(accountId, userId)
} catch (e) {
if (e instanceof AppException && e.code === ResponseCode.ChannelAccountNotAuthorized) {
return res.status(403).json({ message: 'Reconnect this channel to continue', accountId })
}
throw e
} Prevention
- Filter batch jobs and schedulers to exclude ABNORMAL accounts.
- Surface account status in the UI so users reconnect before operations fail.
- Reset status to NORMAL only via a completed OAuth re-authorization.
- Alert when the number of ABNORMAL accounts spikes (platform-wide auth outage).
When it happens
Trigger: Calling refreshCredential or any credential-consuming flow on an account that was previously marked offline/ABNORMAL after a platform auth failure; using an account after the user revoked access or the refresh token expired and the failure handler flagged it.
Common situations: Retry loops re-processing an account already marked offline; schedulers not filtering by account status; users trying to publish content with a disconnected channel without re-linking it.
Related errors
- ChannelAccountAlreadyConnectedToAnotherUser
- ChannelAccessTokenFailed
- No access token available, user needs to authorize
- No access token available, user needs to authorize
- No response from Gemini
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/689bb6d74e91dddf.
Report an issue: GitHub.