{"record":{"id":"90f9c749beae5819","repo":"yikart/AiToEarn","slug":"channelauthrefreshtokenmissing-90f9c7","errorCode":"ChannelAuthRefreshTokenMissing","errorMessage":"ChannelAuthRefreshTokenMissing","messagePattern":"ChannelAuthRefreshTokenMissing","errorType":"error_code","errorClass":"AppException","httpStatus":null,"severity":"error","filePath":"project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/tiktok/tiktok-auth.provider.ts","lineNumber":72,"sourceCode":"    const callback = parseOAuthCallback(input)\n    const codeVerifier = parseAuthCodeVerifier(input)\n    const result = await this.tikTokService.exchangeCode(\n      callback.code,\n      codeVerifier,\n    )\n\n    return {\n      accessToken: result.accessToken,\n      refreshToken: result.refreshToken,\n      expiresAt: result.expiresAt,\n      scope: result.scope,\n      tokenType: 'Bearer',\n    }\n  }\n\n  async refresh(input: RefreshCredentialInput): Promise<CredentialResult> {\n    if (!input.refreshToken) {\n      throw new AppException(ResponseCode.ChannelAuthRefreshTokenMissing)\n    }\n\n    const result = await this.tikTokService.refreshAccessToken(input.refreshToken)\n\n    return {\n      accessToken: result.accessToken,\n      refreshToken: result.refreshToken,\n      expiresAt: result.expiresAt,\n      scope: result.scope,\n      tokenType: 'Bearer',\n    }\n  }\n\n  async revoke(input: RevokeCredentialInput): Promise<void> {\n    await this.tikTokService.revokeAccessToken(input.accessToken)\n  }\n\n  async getProfile(input: CredentialContext): Promise<PlatformAccountProfile> {","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/tiktok/tiktok-auth.provider.ts#L54-L90","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","Fix the connect/exchange path so refresh_token from TikTok's token response is always persisted alongside accessToken.","Skip accounts without refresh tokens in the refresh job instead of throwing, and flag them for re-authorization.","Verify no migration/cleanup deleted the refreshToken column or field."],"exampleFix":"// before\nawait authProvider.refresh({ refreshToken: account.credentials.refreshToken })\n// after\nif (!account.credentials.refreshToken) {\n  await markAccountNeedsReauth(account.id) // skip refresh, require reconnect\n} else {\n  await authProvider.refresh({ refreshToken: account.credentials.refreshToken })\n}","handlingStrategy":"validation","validationCode":"function canRefresh(creds: TikTokCredentials): boolean {\n  return typeof creds.refreshToken === 'string' && creds.refreshToken.length > 0\n}\nif (!canRefresh(account.credentials)) await markAccountNeedsReauth(account.id)","typeGuard":"function hasRefreshToken(c: { refreshToken?: string | null }): c is { refreshToken: string } {\n  return typeof c.refreshToken === 'string' && c.refreshToken.length > 0\n}","tryCatchPattern":"try {\n  await authProvider.refresh({ refreshToken })\n} catch (e) {\n  if (e instanceof AppException && e.code === ResponseCode.ChannelAuthRefreshTokenMissing) {\n    await markAccountNeedsReauth(accountId)\n    return\n  }\n  throw e\n}","preventionTips":["Persist refresh_token from TikTok's token response at connect time — never store accessToken only.","Add a startup/data check flagging credential records missing refreshToken.","Include offline access scope so TikTok issues refresh tokens.","Make re-auth a first-class account state instead of throwing in batch jobs."],"tags":["oauth","tiktok","missing-token","auth"],"backgroundTag":"missing-refresh-token","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}