yikart/AiToEarn · warning · AppException
ResponseCode.ChannelAuthSessionCompleted
ResponseCode.ChannelAuthSessionCompleted
Error message
ChannelAuthSessionCompleted
What it means
Thrown in AuthService.completeCallback when the session status is not Pending — the flow was already completed (or failed/marked). Auth sessions are single-use: once completed and an account connected, further callbacks with the same state are rejected.
Source
Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/auth/auth.service.ts:115
}
async completeCallback(
platform: AccountType,
callbackInput: Omit<AuthCallbackInput, 'session'>,
sessionId: string,
): Promise<AuthCallbackResult> {
const session = await this.redis.getChannelAuthSession<AuthSession>(sessionId)
if (!this.isAccountAuthSessionRecord(session)) {
throw new AppException(ResponseCode.ChannelAuthSessionInvalid)
}
if (this.isSessionExpired(session)) {
throw new AppException(ResponseCode.ChannelAuthSessionInvalid)
}
if (session.platform !== platform) {
throw new AppException(ResponseCode.ChannelAuthPlatformMismatch)
}
if (session.status !== ChannelAuthSessionStatus.Pending) {
throw new AppException(ResponseCode.ChannelAuthSessionCompleted)
}
const provider = this.registry.getAuth(platform)
const credentialResult = await provider.exchangeCode({
...callbackInput,
session,
})
const credentialContext = credentialResult.accessToken
? this.toCredentialContext(credentialResult)
: undefined
const profile = credentialResult.profile
?? (credentialContext
? await provider.getProfile(credentialContext)
: undefined)
if (!profile) {
throw new AppException(ResponseCode.ChannelAuthPlatformUidMissing)
}View on GitHub (pinned to d3aa8bea5b)
Solutions
- Treat this as benign idempotency: return the already-connected account instead of restarting the flow if appropriate for your UX.
- Restart the auth flow to link additional or different accounts.
- Guard the client against double submission (disable the button, avoid refresh on POST callbacks).
- If provider retries are expected, configure the platform callback to tolerate completed sessions by looking up the session's stored accountId/accounts.
Example fix
// before: refreshing the callback URL re-runs the flow
GET /api/channels/auth/callback/instagram?code=abc&state=s1 // 2nd time -> error
// after: on ChannelAuthSessionCompleted, show the existing result instead
try { await completeCallback(...) }
catch (e) {
if (getErrorCode(e) === ResponseCode.ChannelAuthSessionCompleted) {
showAlreadyConnectedMessage() // do not restart flow
}
} Defensive patterns
Strategy: try-catch
Try / catch
try {
await completeCallback(platform, input, sessionId)
}
catch (e) {
if (getErrorCode(e) === ResponseCode.ChannelAuthSessionCompleted) {
return renderAlreadyConnectedView(sessionId) // idempotent success
}
throw e
} Prevention
- Disable submit buttons after first click
- Avoid cacheable callback GET responses so refresh doesn't replay
- Treat completed-session callbacks as idempotent successes server-side
- Deduplicate provider retries using the authorization code/state as idempotency key
When it happens
Trigger: completeCallback (or the callback endpoint) is hit a second time with the same state/sessionId — e.g. the provider retries the redirect, the user double-clicks/back-button refreshes the callback URL, or the same authorization code is delivered twice.
Common situations: Browser refresh on the callback page resubmitting the OAuth code; provider-side automatic retry of the redirect; user replaying a success URL from history; duplicate webhook/callback delivery.
Related errors
- ResponseCode.ChannelAuthSessionInvalid
- ResponseCode.ChannelAuthPlatformMismatch
- ResponseCode.ChannelAuthSelectableAccountsNotFound
- 无效的状态码
- No response from Gemini
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/80f3d7e26c1f824f.
Report an issue: GitHub.