gitroomhq/postiz-app · error · Error

User info request failed: ${error}

Error message

User info request failed: ${error}

What it means

Generic OAuth provider base class: after a successful token exchange, the provider's user-info endpoint returned a non-2xx when called with the access_token (Accept: application/json). The provider's error body is appended to the message.

Source

Thrown at apps/backend/src/services/auth/providers/oauth.provider.ts:89

      throw new Error(`Token request failed: ${error}`);
    }

    const { access_token } = await response.json();
    return access_token;
  }

  async getUser(access_token: string): Promise<{ email: string; id: string }> {
    const { userInfoUrl } = this.getConfig();
    const response = await fetch(`${userInfoUrl}`, {
      headers: {
        Authorization: `Bearer ${access_token}`,
        Accept: 'application/json',
      },
    });

    if (!response.ok) {
      const error = await response.text();
      throw new Error(`User info request failed: ${error}`);
    }

    const { email, sub: id } = await response.json();
    return { email, id };
  }
}

View on GitHub (pinned to 0f1647f749)

Solutions

  1. Check the embedded status/error: 401 -> token invalid (re-do flow), 403 -> missing scope/consent, 429/5xx -> retry with backoff
  2. Verify the authorization request includes the scopes the userinfo endpoint requires (usually email/profile)
  3. Confirm the userinfo URL in the provider config is current (providers do deprecate API versions)
  4. Add a single retry with jitter for transient 5xx/429 responses

Example fix

// before — single attempt, any failure throws
const res = await fetch(userinfoUrl, { headers: { Authorization: `Bearer ${token}` } });

// after — one guarded retry for transient failures
let res = await fetch(userinfoUrl, { headers });
if (res.status >= 500 || res.status === 429) {
  await sleep(1000);
  res = await fetch(userinfoUrl, { headers });
}
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

null

Try / catch

try {
  const profile = await provider.getUser(token);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('User info request failed:')) {
    const body = e.message.slice('User info request failed:'.length);
    if (/(429|5xx|unavailable)/.test(body)) {
      await sleep(1000);
      return provider.getUser(token); // single retry
    }
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling the provider's userinfo URL with an access token that is expired, revoked, lacks the required scope, or was issued for a different app; also provider-side errors (rate limiting, API downtime).

Common situations: Token scope lacks email/profile so userinfo returns 403; access token TTL of seconds and exchange->userinfo latency exceeds it; provider app permissions not approved for the userinfo scope; provider API deprecation changing the userinfo URL.

Related errors


AI-assisted analysis of gitroomhq/postiz-app@0f1647f749 (2026-08-27). Data as JSON: /api/errors/6b084095f43f416b. Report an issue: GitHub.