yikart/AiToEarn · error · RelayAccountException
RelayAccountException
Error message
RelayAccountException
What it means
RelayAccountException is thrown by getCredentialAccount when the located account has a relayAccountRef, meaning the channel account is managed through the Relay service rather than holding local OAuth credentials. The exception carries the relayAccountRef and originalAccountId (plus an accountIdMap) so upstream Relay middleware can route the operation to the Relay server. Its message is 'This account requires relay'.
Source
Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/auth/auth.service.ts:626
}
return {
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 trueView on GitHub (pinned to d3aa8bea5b)
Solutions
- Route the operation through the Relay client/service instead of the local credential flow; use the exception's relayAccountRef/originalAccountId to map the id.
- Catch RelayAccountException explicitly and translate it into a Relay API call (the accountIdMap maps original account ids to relay ids).
- If the account should not be Relay-managed, re-authorize it locally so the account is recreated without relayAccountRef.
- Check relayClientService configuration — enabling Relay changes which accounts local auth paths may touch.
Example fix
// before
const cred = await authService.refreshCredential(accountId)
// after
try {
cred = await authService.refreshCredential(accountId)
} catch (e) {
if (e instanceof RelayAccountException) {
cred = await relayClientService.refreshCredential(e.relayAccountRef)
} else throw e
} Defensive patterns
Strategy: type-guard
Validate before calling
const account = await accountRepo.getAccountById(accountId)
if (account?.relayAccountRef) {
return relayClientService.refreshCredential(account.relayAccountRef)
} Type guard
function isRelayAccount(a: { relayAccountRef?: string | null }): boolean {
return typeof a.relayAccountRef === 'string' && a.relayAccountRef.length > 0
} Try / catch
try {
return await authService.refreshCredential(accountId)
} catch (e) {
if (e instanceof RelayAccountException) {
return relayClientService.refreshCredential(e.relayAccountRef)
}
throw e
} Prevention
- Check relayAccountRef before any local credential operation on accounts.
- Keep Relay and direct-auth code paths separated behind one facade that branches once.
- Document in the account model that relayAccountRef means 'do not touch local credentials'.
- Test mixed deployments (relay on/off) in CI.
When it happens
Trigger: Any credential/auth operation on an account whose document contains a non-empty relayAccountRef — e.g. refreshCredential on a Relay-managed account; direct backend OAuth integration is refused because tokens live on the Relay server.
Common situations: Deployment where relayClientService is enabled and accounts were onboarded via Relay, but a code path (scheduler, manual script, old endpoint) still calls the local credential API; mixing Relay-onboarded accounts with non-Relay account management tools.
Related errors
- body.code
- AiCallFailed
- Relay API error [${body.code}]: ${body.message}
- Relay uploadSign returned no uploadUrl: ${JSON.stringify(sig
- InvalidModel
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/1dfc70c5c0fdc39b.
Report an issue: GitHub.