{"record":{"id":"bfed6a81e0a2c157","repo":"yikart/AiToEarn","slug":"error-bfed6a","errorCode":null,"errorMessage":"无效的账号或刷新令牌丢失","messagePattern":"无效的账号或刷新令牌丢失","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.auth.service.ts","lineNumber":507,"sourceCode":"   */\n  async getUserAccessToken(accountId: string): Promise<string> {\n    this.logger.log(`获取TikTok访问令牌: accountId=${accountId}`);\n\n    // 先检查Redis缓存\n    const cachedToken = await this.redisService.get(`tiktok:accessToken:${accountId}`);\n    if (cachedToken && cachedToken.access_token) {\n      this.logger.log(\"从Redis获取到有效令牌\");\n      return cachedToken.access_token;\n    }\n\n    // 如果缓存中没有，尝试刷新\n    const accountTokenInfo = await this.accountTokenModel.findOne({\n      accountId: accountId,\n      platform: TokenPlatform.TIKTOK\n    });\n\n    if (!accountTokenInfo || !accountTokenInfo.refreshToken) {\n      throw new BadRequestException('无效的账号或刷新令牌丢失');\n    }\n\n    // 刷新并获取新令牌\n    const refreshResult = await this.refreshAccessToken(\n      accountTokenInfo.userId,\n      accountTokenInfo.accountId,\n      accountTokenInfo.refreshToken\n    );\n\n    // 刷新后再次从Redis获取\n    const newToken = await this.redisService.get(`tiktok:accessToken:${accountId}`);\n    if (!newToken || !newToken.access_token) {\n      throw new BadRequestException('刷新令牌后未能获取访问令牌');\n    }\n\n    return newToken.access_token;\n  }\n","sourceCodeStart":489,"sourceCodeEnd":525,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/plat/tiktok/tiktok.auth.service.ts#L489-L525","documentation":"A BadRequestException thrown by getUserAccessToken when no account token record exists in MongoDB for the given accountId+TIKTOK platform, or the record exists but its refreshToken field is empty. This is a local pre-condition check before attempting any refresh call.","triggerScenarios":"Calling GET /tiktok/access-token (getUserAccessToken) with an accountId that was never authorized, the account was deleted, or the stored document has refreshToken unset/null — e.g. it was cleared after a previous invalid_refresh_token failure.","commonSituations":"Caller passes a userId instead of accountId, account authorized on a different environment (cn vs ai) with a separate database, refresh token scrubbed by cleanup logic, account revoked in TikTok and the app cleared tokens.","solutions":["Verify the accountId exists in the accountToken collection with platform='TIKTOK' and a non-empty refreshToken.","If refreshToken is missing, ask the user to complete the TikTok OAuth flow again via /tiktok/auth-url.","Confirm the caller passes the correct accountId (not userId) and that you're querying the right environment's database.","Check whether token cleanup/invalidation logic recently removed the refreshToken field."],"exampleFix":"// before\nif (!accountTokenInfo || !accountTokenInfo.refreshToken) {\n  throw new BadRequestException('无效的账号或刷新令牌丢失');\n}\n// after\nif (!accountTokenInfo) {\n  throw new BadRequestException(`账号 ${accountId} 未绑定 TikTok，请先完成授权`);\n}\nif (!accountTokenInfo.refreshToken) {\n  throw new BadRequestException(`账号 ${accountId} 刷新令牌已失效，请重新授权`);\n}","handlingStrategy":"validation","validationCode":"// before calling access-token endpoint\nconst rec = await db.accountTokens.findOne({ accountId, platform: 'TIKTOK' });\nif (!rec?.refreshToken) {\n  throw new Error(`account ${accountId} has no TikTok refresh token; run OAuth flow first`);\n}","typeGuard":"function hasUsableTikTokAccount(t: unknown): t is { accountId: string; refreshToken: string } {\n  return typeof t === 'object' && t !== null &&\n    typeof (t as any).refreshToken === 'string' && (t as any).refreshToken.length > 0;\n}","tryCatchPattern":"try {\n  const token = await api.getTikTokAccessToken(accountId);\n} catch (e) {\n  if (/刷新令牌丢失/.test((e as Error).message)) {\n    const authUrl = await api.getTikTokAuthUrl(userId, mail);\n    return redirectUserToReauth(authUrl);\n  }\n  throw e;\n}","preventionTips":["Verify account binding (accountId + platform) before calling token endpoints.","Pass accountId, not userId, to the lookup.","Keep environment databases separate and query the right one.","Surface a re-auth CTA to users when the refresh token is missing."],"tags":["tiktok","missing-token","database","precondition"],"backgroundTag":"missing-refresh-token","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}