{"record":{"id":"636b93ba797caeea","repo":"yikart/AiToEarn","slug":"channelauthrefreshtokenmissing","errorCode":"ChannelAuthRefreshTokenMissing","errorMessage":"ResponseCode.ChannelAuthRefreshTokenMissing","messagePattern":"ResponseCode\\.ChannelAuthRefreshTokenMissing","errorType":"exception","errorClass":"AppException","httpStatus":null,"severity":"error","filePath":"project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/bilibili/bilibili-auth.provider.ts","lineNumber":46,"sourceCode":"    const url = this.bilibiliService.generateAuthUrl(input.state)\n\n    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.bilibiliService.exchangeCode(callback.code)\n\n    return {\n      accessToken: result.accessToken,\n      refreshToken: result.refreshToken,\n      expiresAt: result.expiresAt,\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.bilibiliService.refreshAccessToken(input.refreshToken)\n\n    return {\n      accessToken: result.accessToken,\n      refreshToken: result.refreshToken,\n      expiresAt: result.expiresAt,\n    }\n  }\n\n  async revoke(input: RevokeCredentialInput): Promise<void> {\n    await this.bilibiliService.revokeToken(input.accessToken)\n  }\n\n  async getProfile(input: CredentialContext): Promise<PlatformAccountProfile> {\n    const userInfo = await this.bilibiliService.getUserInfo(input.accessToken)\n","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/apps/aitoearn-server/src/core/channels/platforms/bilibili/bilibili-auth.provider.ts#L28-L64","documentation":"BilibiliAuthProvider.refresh requires a refresh token to renew credentials. If input.refreshToken is falsy it throws AppException ChannelAuthRefreshTokenMissing, because the OAuth refresh endpoint cannot be called without it.","triggerScenarios":"Credential refresh flow invoked (e.g. token expiry handler or getUserAccessToken -> refresh) with a stored credential that has accessToken but no refreshToken saved during initial authorization.","commonSituations":"Old credentials created before refreshToken persistence was added; account connected via a flow that stored only the access token; refreshToken cleared after a failed refresh (Bilibili invalidates it); DB row with null refresh_token.","solutions":["Re-run the Bilibili OAuth authorization flow to obtain a fresh accessToken+refreshToken pair.","Check the stored credential record and backfill refreshToken where missing.","Before calling refresh, guard: if (!credential.refreshToken) trigger re-auth instead of refresh.","Persist refreshToken at exchange time and never overwrite it with null on partial updates."],"exampleFix":"// before\nawait authProvider.refresh({ accessToken }) // refreshToken undefined\n\n// after\nif (!credential.refreshToken) {\n  await startReauth(credential.userId)\n} else {\n  await authProvider.refresh(credential)\n}","handlingStrategy":"validation","validationCode":"if (!credential.refreshToken) {\n  await startReauthorization(credential.userId)\n  return\n}","typeGuard":"function hasRefreshToken(c: Credential): c is Credential & { refreshToken: string } {\n  return typeof c.refreshToken === 'string' && c.refreshToken.length > 0\n}","tryCatchPattern":"try {\n  await authProvider.refresh(credential)\n} catch (e) {\n  if (e.code === 'ChannelAuthRefreshTokenMissing') {\n    await markCredentialNeedsReauth(credential.userId)\n    return notifyReauthRequired(credential.userId)\n  }\n  throw e\n}","preventionTips":["Persist refreshToken immediately during OAuth exchange and never null it in partial updates","Detect legacy credentials missing refreshToken and force re-auth proactively","Monitor credential rows with empty refresh_token and alert"],"tags":["oauth","bilibili","auth","missing-token"],"backgroundTag":"missing-refresh-token","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}