yikart/AiToEarn · error · Error

Failed to refresh access token

Error message

Failed to refresh access token

What it means

refreshAccessToken exchanges a Google refresh token for a new access token via the Google OAuth endpoint. Any failure in that HTTP call (invalid grant, network error, expired/revoked refresh token, bad client credentials) is caught, logged, and rethrown as a generic Error('Failed to refresh access token'), discarding the original error details from the caller.

Source

Thrown at project/aitoearn-electron/server/src/modules/plat/google/google.service.ts:765

      // const expires = 30 * 24 * 60 * 60
      console.log("accessTokenInfo:---", accessTokenInfo);
      // console.log("expires:---", accessTokenInfo.expires_in);
      this.redisService.setKey(
        `google:accessToken:${userId}`,
        accessTokenInfo,
        accessTokenInfo.expires_in
      );

      // return result.data;
      return systemToken;


      // 返回新的 access token 和其他信息
      // return response.data;  // 包含新的 access_token、expires_in、token_type 等信息
    } catch (err) {
      console.log('Error while refreshing access token', err);
      throw new Error('Failed to refresh access token');
    }
  }

  /**
   * 查询用户已授权权限列表
   * @returns
   */
  async getAccountScopes(accessToken: string) {
    try {
      // 初始化 OAuth2 客户端
      // const oAuth2Client = new google.auth.OAuth2();
      this.oauth2Client.setCredentials({
        access_token: accessToken,
      });

      // 通过访问 token 查询 token 信息
      const tokenInfo = await google.oauth2('v2').tokeninfo({
        access_token: accessToken,

View on GitHub (pinned to d3aa8bea5b)

Solutions

  1. Uncomment/inspect the real err — console.log already prints it; check for 'invalid_grant' meaning the refresh token must be re-obtained by re-authenticating the user
  2. Confirm GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET match the OAuth client that issued the refresh token
  3. If refresh token is revoked/expired, mark the account as needing re-authorization and prompt the user to reconnect
  4. Verify server outbound network access to oauth2.googleapis.com
  5. Include err.response?.data in the thrown message for diagnosability

Example fix

// before
} catch (err) {
  console.log('Error while refreshing access token', err);
  throw new Error('Failed to refresh access token');
}
// after
} catch (err) {
  const detail = err?.response?.data?.error || err?.message;
  throw new Error(`Failed to refresh access token: ${detail}`, { cause: err });
}
Defensive patterns

Strategy: retry

Validate before calling

if (!refreshToken) {
  throw new Error('No refresh token stored for this account; re-authorization required');
}

Try / catch

try {
  return await googleService.getUserAccessToken(account);
} catch (err) {
  if (err.message.includes('Failed to refresh access token')) {
    await markAccountNeedsReauth(account.id); // invalid_grant: token dead
    throw new UnauthorizedException('Please reconnect your Google account');
  }
  throw err; // transient: let caller retry
}

Prevention

When it happens

Trigger: Calling refreshAccessToken (directly or via getUserAccessToken) when the POST to https://oauth2.googleapis.com/token fails: Google returns 400 invalid_grant (refresh token expired/revoked), 401 invalid_client, network timeout, or the response lacks access_token.

Common situations: User revoked app access in Google account settings (invalid_grant); refresh token older than 7 days for apps in testing mode; GOOGLE_CLIENT_SECRET env var changed or missing; server clock skew causing token issues; sandbox without network access to Google.

Related errors


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