yikart/AiToEarn · error · AppException

ChannelAuthRefreshTokenMissing

ChannelAuthRefreshTokenMissing

Error message

ChannelAuthRefreshTokenMissing

What it means

TikTok credential refresh requires a stored refresh token; refresh() immediately throws ChannelAuthRefreshTokenMissing when input.refreshToken is absent. Without it there is no way to obtain a new access token from TikTok's OAuth endpoint.

Source

Thrown at project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/tiktok/tiktok-auth.provider.ts:72

    const callback = parseOAuthCallback(input)
    const codeVerifier = parseAuthCodeVerifier(input)
    const result = await this.tikTokService.exchangeCode(
      callback.code,
      codeVerifier,
    )

    return {
      accessToken: result.accessToken,
      refreshToken: result.refreshToken,
      expiresAt: result.expiresAt,
      scope: result.scope,
      tokenType: 'Bearer',
    }
  }

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

    const result = await this.tikTokService.refreshAccessToken(input.refreshToken)

    return {
      accessToken: result.accessToken,
      refreshToken: result.refreshToken,
      expiresAt: result.expiresAt,
      scope: result.scope,
      tokenType: 'Bearer',
    }
  }

  async revoke(input: RevokeCredentialInput): Promise<void> {
    await this.tikTokService.revokeAccessToken(input.accessToken)
  }

  async getProfile(input: CredentialContext): Promise<PlatformAccountProfile> {

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Check the stored credential record for the TikTok account — if refreshToken is empty, re-run the TikTok OAuth connect flow to obtain and persist a new refresh token.
  2. Fix the connect/exchange path so refresh_token from TikTok's token response is always persisted alongside accessToken.
  3. Skip accounts without refresh tokens in the refresh job instead of throwing, and flag them for re-authorization.
  4. Verify no migration/cleanup deleted the refreshToken column or field.

Example fix

// before
await authProvider.refresh({ refreshToken: account.credentials.refreshToken })
// after
if (!account.credentials.refreshToken) {
  await markAccountNeedsReauth(account.id) // skip refresh, require reconnect
} else {
  await authProvider.refresh({ refreshToken: account.credentials.refreshToken })
}
Defensive patterns

Strategy: validation

Validate before calling

function canRefresh(creds: TikTokCredentials): boolean {
  return typeof creds.refreshToken === 'string' && creds.refreshToken.length > 0
}
if (!canRefresh(account.credentials)) await markAccountNeedsReauth(account.id)

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 authProvider.refresh({ refreshToken })
} catch (e) {
  if (e instanceof AppException && e.code === ResponseCode.ChannelAuthRefreshTokenMissing) {
    await markAccountNeedsReauth(accountId)
    return
  }
  throw e
}

Prevention

When it happens

Trigger: Calling tiktokAuthProvider.refresh({ refreshToken: undefined }) — typically for an account whose stored credentials only contain an accessToken, or where refresh_token was never persisted during connect.

Common situations: Cron/refresh job iterating accounts where TikTok connect flow didn't save refreshToken; manual DB edit wiped the field; account connected via a flow that returns long-lived tokens without refresh tokens; field renamed during a schema migration.

Related errors


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