{"record":{"id":"1ee6b0fcd92265a4","repo":"yikart/AiToEarn","slug":"no-access-token-available-user-needs-to-authorize","errorCode":null,"errorMessage":"No access token available, user needs to authorize","messagePattern":"No access token available, user needs to authorize","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"project/aitoearn-electron/server/src/modules/plat/twitter/twitter.auth.service.ts","lineNumber":569,"sourceCode":"        { $unset: { 'refreshToken': 1, 'expiresAt': 1 } }\n      );\n\n      return true;\n    } catch (error) {\n      console.error('撤销Twitter授权失败:', error);\n      return false;\n    }\n  }\n\n  /**\n   * 获取初始化后的Twitter API客户端\n   * @param accountId 账号ID\n   * @returns 初始化后的客户端\n   */\n  async getTwitterClient(accountId: string): Promise<any> {\n    const accessToken = await this.getUserAccessToken(accountId);\n    if (!accessToken) {\n      throw new Error('No access token available, user needs to authorize');\n    }\n\n    // 返回一个简单的API客户端，可以根据需要扩展\n    return {\n      headers: {\n        Authorization: `Bearer ${accessToken}`\n      },\n      baseUrl: TWITTER_API_V2_BASE_URL,\n      async get(endpoint: string, params = {}) {\n        // 这里可以实现实际的API调用逻辑\n        // 或者使用第三方Twitter客户端库\n      }\n    };\n  }\n}\n","sourceCodeStart":551,"sourceCodeEnd":585,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-electron/server/src/modules/plat/twitter/twitter.auth.service.ts#L551-L585","documentation":"getTwitterClient() calls getUserAccessToken(accountId) and, if it receives a falsy token, throws a plain Error('No access token available, user needs to authorize'). In practice this is nearly unreachable because getUserAccessToken throws a BadRequestException first (errors 450/451) when it cannot obtain a token; this line only fires if the token chain returns an empty string without throwing. It signals that the end user must complete Twitter OAuth before a client can be built.","triggerScenarios":"Programmatic calls to getTwitterClient with an accountId whose cached and refreshed tokens resolve to an empty string — practically only if getUserAccessToken's error paths are bypassed or refactored to return empty instead of throwing.","commonSituations":"Internal service code or scripts calling getTwitterClient directly for an unauthorized account; refactors that change getUserAccessToken to return null/'' instead of throwing; calling before the OAuth callback stored any token.","solutions":["Ensure the account completed OAuth: GET /plat/twitter/auth/url then the callback, so tokens exist in Redis/Mongo","Check authorization first via GET /plat/twitter/auth/check?accountId=... and surface a 're-authorize' flow to the user instead of calling getTwitterClient","Catch this Error (and the BadRequestException from getUserAccessToken) and return 401-style guidance telling the user to re-authorize","Prefer throwing an UnauthorizedException instead of generic Error so HTTP semantics are correct"],"exampleFix":"// before\nconst accessToken = await this.getUserAccessToken(accountId);\nif (!accessToken) {\n  throw new Error('No access token available, user needs to authorize');\n}\n// after\nconst accessToken = await this.getUserAccessToken(accountId);\nif (!accessToken) {\n  throw new UnauthorizedException('Twitter account not authorized; complete OAuth first');\n}","handlingStrategy":"try-catch","validationCode":"const authorized = await twitterAuthService.isAuthorized(accountId);\nif (!authorized) {\n  const { url } = await twitterAuthService.getAuthorizationUrl(userId, mail);\n  // redirect the user to url to authorize\n}","typeGuard":"function hasAccessToken(v: unknown): v is { headers: { Authorization: string } } {\n  return !!v && typeof v === 'object'\n    && 'headers' in v\n    && typeof (v as any).headers?.Authorization === 'string'\n    && (v as any).headers.Authorization.startsWith('Bearer ');\n}","tryCatchPattern":"try {\n  const client = await twitterAuthService.getTwitterClient(accountId);\n} catch (e) {\n  if (e instanceof UnauthorizedException || e instanceof BadRequestException || e.message.includes('No access token')) {\n    return { needsAuthorization: true };\n  }\n  throw e;\n}","preventionTips":["Check /plat/twitter/auth/check before building a client","Build a UI state that prompts re-authorization instead of calling APIs for unauthorized accounts","Never assume a stored accountId is still authorized after revocation or token expiry","Replace the generic Error with UnauthorizedException for correct HTTP semantics"],"tags":["oauth","twitter","authorization"],"backgroundTag":"user-not-authorized","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}