{"record":{"id":"6a8a0d25cb09079a","repo":"yikart/AiToEarn","slug":"channelauthrefreshtokenmissing-6a8a0d","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/kwai/kwai-auth.provider.ts","lineNumber":48,"sourceCode":"    return { url, state: input.state, redirectUri: this.config.redirectUri }\n  }\n\n  async exchangeCode(input: AuthCallbackInput): Promise<CredentialResult> {\n    const callback = parseOAuthCallback(input)\n    const result = await this.kwaiService.exchangeCode(callback.code)\n\n    return {\n      accessToken: result.accessToken,\n      refreshToken: result.refreshToken,\n      expiresAt: result.expiresAt,\n      scope: result.scope,\n      platformUid: result.openId,\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.kwaiService.refreshToken(input.refreshToken)\n\n    return {\n      accessToken: result.accessToken,\n      refreshToken: result.refreshToken,\n      expiresAt: result.expiresAt,\n      scope: result.scope,\n    }\n  }\n\n  async revoke(_input: RevokeCredentialInput): Promise<void> {\n    // Kwai does not support token revocation via API\n    this.logger.warn('Kwai does not support token revocation via API')\n  }\n\n  async getProfile(input: CredentialContext): Promise<PlatformAccountProfile> {","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/kwai/kwai-auth.provider.ts#L30-L66","documentation":"Kwai credential refresh was attempted without a stored refresh token; the provider cannot exchange anything for a new access token.","triggerScenarios":"refresh() is called with input.refreshToken falsy — the Kwai OAuth grant didn't yield a refresh token or it wasn't persisted on the credential.","commonSituations":"Kwai OpenAPI app not approved for offline access (refresh_token not returned); credential saved before refresh_token existed; token record truncated during storage/migration; refresh token already consumed and cleared.","solutions":["Re-run the Kwai OAuth authorize flow to get a fresh refresh_token and persist it","Confirm the Kwai app has permission/scope granting refresh tokens","Check the credential save path stores result.refreshToken","If the refresh token was consumed/expired, full re-auth is required"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if (!credential.refreshToken) {\n  throw new Error('No Kwai refresh token stored; user must re-authorize the Kwai channel')\n}","typeGuard":"function hasKwaiRefreshToken(c: { refreshToken?: string | null }): c is { refreshToken: string } {\n  return typeof c.refreshToken === 'string' && c.refreshToken.length > 0\n}","tryCatchPattern":"try {\n  await provider.refresh(input)\n} catch (e) {\n  if (e.code === 'ChannelAuthRefreshTokenMissing') {\n    await markChannelNeedsReauth(accountId, 'kwai')\n  } else throw e\n}","preventionTips":["Persist kwai refresh_token at the initial OAuth callback","Verify the Kwai app scope returns refresh_token (offline access)","Never overwrite a valid refresh token with null during partial updates","Detect expired/consumed refresh tokens and trigger full re-auth"],"tags":["kwai","oauth","refresh-token","missing-data"],"backgroundTag":"missing-refresh-token","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}