yikart/AiToEarn · error · Error

No access token available, user needs to authorize

Error message

No access token available, user needs to authorize

What it means

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.

Source

Thrown at project/aitoearn-electron/server/src/modules/plat/twitter/twitter.auth.service.ts:569

        { $unset: { 'refreshToken': 1, 'expiresAt': 1 } }
      );

      return true;
    } catch (error) {
      console.error('撤销Twitter授权失败:', error);
      return false;
    }
  }

  /**
   * 获取初始化后的Twitter API客户端
   * @param accountId 账号ID
   * @returns 初始化后的客户端
   */
  async getTwitterClient(accountId: string): Promise<any> {
    const accessToken = await this.getUserAccessToken(accountId);
    if (!accessToken) {
      throw new Error('No access token available, user needs to authorize');
    }

    // 返回一个简单的API客户端,可以根据需要扩展
    return {
      headers: {
        Authorization: `Bearer ${accessToken}`
      },
      baseUrl: TWITTER_API_V2_BASE_URL,
      async get(endpoint: string, params = {}) {
        // 这里可以实现实际的API调用逻辑
        // 或者使用第三方Twitter客户端库
      }
    };
  }
}

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Ensure the account completed OAuth: GET /plat/twitter/auth/url then the callback, so tokens exist in Redis/Mongo
  2. Check authorization first via GET /plat/twitter/auth/check?accountId=... and surface a 're-authorize' flow to the user instead of calling getTwitterClient
  3. Catch this Error (and the BadRequestException from getUserAccessToken) and return 401-style guidance telling the user to re-authorize
  4. Prefer throwing an UnauthorizedException instead of generic Error so HTTP semantics are correct

Example fix

// before
const accessToken = await this.getUserAccessToken(accountId);
if (!accessToken) {
  throw new Error('No access token available, user needs to authorize');
}
// after
const accessToken = await this.getUserAccessToken(accountId);
if (!accessToken) {
  throw new UnauthorizedException('Twitter account not authorized; complete OAuth first');
}
Defensive patterns

Strategy: try-catch

Validate before calling

const authorized = await twitterAuthService.isAuthorized(accountId);
if (!authorized) {
  const { url } = await twitterAuthService.getAuthorizationUrl(userId, mail);
  // redirect the user to url to authorize
}

Type guard

function hasAccessToken(v: unknown): v is { headers: { Authorization: string } } {
  return !!v && typeof v === 'object'
    && 'headers' in v
    && typeof (v as any).headers?.Authorization === 'string'
    && (v as any).headers.Authorization.startsWith('Bearer ');
}

Try / catch

try {
  const client = await twitterAuthService.getTwitterClient(accountId);
} catch (e) {
  if (e instanceof UnauthorizedException || e instanceof BadRequestException || e.message.includes('No access token')) {
    return { needsAuthorization: true };
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31). Data as JSON: /api/errors/1ee6b0fcd92265a4. Report an issue: GitHub.