yikart/AiToEarn · error · AppException

ChannelAuthRefreshTokenMissing

ChannelAuthRefreshTokenMissing

Error message

ChannelAuthRefreshTokenMissing

What it means

The WeChat Official Account auth provider refreshes access tokens via the stored refresh token. Because WeChat OAuth refresh requires a refresh_token, calling refresh without one throws AppException(ResponseCode.ChannelAuthRefreshTokenMissing).

Source

Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/wechat/wechat-official/wechat-official-auth.provider.ts:68

    const callback = parseOAuthCallback(input)
    const result = await this.wechatService.exchangeOfficialCode(callback.code)

    return {
      accessToken: result.accessToken,
      refreshToken: result.refreshToken,
      expiresAt: new Date(Date.now() + result.expiresIn * 1000),
      scope: result.scope,
      platformUid: result.openId,
      raw: {
        openId: result.openId,
        unionId: result.unionId,
      },
    }
  }

  async refresh(input: RefreshCredentialInput): Promise<CredentialResult> {
    if (!input.refreshToken) {
      throw new AppException(ResponseCode.ChannelAuthRefreshTokenMissing)
    }

    const result = await this.wechatService.refreshOfficialToken(input.refreshToken)

    return {
      accessToken: result.accessToken,
      refreshToken: result.refreshToken,
      expiresAt: new Date(Date.now() + result.expiresIn * 1000),
      scope: result.scope,
    }
  }

  async revoke(_input: RevokeCredentialInput): Promise<void> {
    // WeChat Official Account OAuth does not support server-side token revocation.
    // Tokens expire naturally (2 hours for access, 30 days for refresh).
    this.logger.log('WeChat Official Account tokens expire naturally; no revocation needed')
  }

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Require the user to re-authorize the WeChat Official Account to obtain a fresh token pair
  2. Persist the refresh_token returned by WeChat's OAuth code exchange into the credential store
  3. Add a check upstream to route channels lacking refreshToken to re-auth instead of refresh
  4. Audit credential rows for null refresh_token and mark them for re-auth

Example fix

// before
await authProvider.refresh({ accessToken })
// after
if (!credential.refreshToken) {
  await startReauthFlow(channelId)
} else {
  await authProvider.refresh(credential)
}
Defensive patterns

Strategy: validation

Validate before calling

if (!credential.refreshToken) {
  scheduleReauth(channelId)
} else {
  await authProvider.refresh(credential)
}

Type guard

function hasRefreshToken(c: unknown): c is { refreshToken: string } {
  return typeof (c as any)?.refreshToken === 'string' && (c as any).refreshToken.length > 0
}

Try / catch

try {
  await authProvider.refresh(input)
} catch (e) {
  if (e?.code === 'ChannelAuthRefreshTokenMissing') {
    await startReauthFlow(input.channelId)
    return
  }
  throw e
}

Prevention

When it happens

Trigger: Invoking refresh with input.refreshToken undefined/null — typically when the stored credential never captured a refresh token or it was lost during credential rotation.

Common situations: Legacy channel records created before refresh tokens were persisted, credentials imported manually, WeChat returning no refresh_token in the initial OAuth exchange (web-app vs snsapi flows).

Related errors


AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31). Data as JSON: /api/errors/8f938ac1ae6ef46c. Report an issue: GitHub.