immich-app/immich · error · InternalServerErrorException

Error in OAuth discovery

Error message

Error in OAuth discovery: ${error}

What it means

getClient performs OIDC discovery (fetching /.well-known/openid-configuration via openid-client) and wraps failures in an InternalServerErrorException with this message. Any discovery failure — DNS failure, TLS error, timeout, non-OIDC response, or AggregateError across multiple resolved addresses — results in this 500 error.

Solutions

  1. Verify the issuerUrl is reachable from the server: curl <issuerUrl>/.well-known/openid-configuration
  2. Check DNS/firewall/egress rules and whether IPv6 resolution is failing (AggregateError)
  3. Set allowInsecureRequests when the issuer is http:// (local/dev only)
  4. Increase the discovery timeout or check provider health/status page

Example fix

// before
discovery(server, issuerUrl) // fails on http://localhost provider
// after
discovery(server, issuerUrl, { execute: [allowInsecureRequests] }) // dev-only insecure discovery
Defensive patterns

Strategy: retry

Validate before calling

const wellKnown = `${issuerUrl.replace(/\/$/, '')}/.well-known/openid-configuration`;
const res = await fetch(wellKnown).catch(() => null);
if (!res?.ok) throw new Error(`Discovery endpoint unreachable: ${wellKnown}`);

Try / catch

try {
  await oauthRepo.getClient({ issuerUrl /* ... */ });
} catch (e) {
  if (String(e.message).startsWith('Error in OAuth discovery')) {
    logger.error('Discovery failed — check issuer URL reachability/egress', { cause: (e as any).cause });
    throw new ServiceUnavailableError('Identity provider unreachable');
  }
  throw e;
}
// plus: retry with exponential backoff for transient network failures

Prevention

When it happens

Trigger: First client request triggers discovery against issuerUrl; the well-known document is unreachable, times out (per configured timeout), returns invalid metadata, or all candidate addresses fail (AggregateError, e.g. IPv6 then IPv4 refused).

Common situations: Issuer URL typo or internal-only provider unreachable from the server; firewall blocks egress; provider briefly down; allowInsecureRequests not enabled while testing against http:// local IdP; DNS resolving to unreachable IPv6 addresses.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15). Data as JSON: /api/errors/043ae2c4392af2d8. Report an issue: GitHub.

Appendix: source

Thrown at server/src/repositories/oauth.repository.ts:223

    try {
      return await discovery(
        new URL(issuerUrl),
        clientId,
        {
          client_secret: clientSecret,
          response_types: ['code'],
          userinfo_signed_response_alg: profileSigningAlgorithm === 'none' ? undefined : profileSigningAlgorithm,
          id_token_signed_response_alg: signingAlgorithm,
        },
        this.getTokenAuthMethod(tokenEndpointAuthMethod, clientSecret),
        {
          execute: allowInsecureRequests ? [allowInsecureRequestsExecute] : [],
          timeout,
        },
      );
    } catch (error: any | AggregateError) {
      this.logger.error('Error in OAuth discovery', error);
      throw new InternalServerErrorException(`Error in OAuth discovery: ${error}`, { cause: error });
    }
  }

  private getTokenAuthMethod(tokenEndpointAuthMethod: OAuthTokenEndpointAuthMethod, clientSecret?: string) {
    if (!clientSecret) {
      return None();
    }

    switch (tokenEndpointAuthMethod) {
      case OAuthTokenEndpointAuthMethod.ClientSecretPost: {
        return ClientSecretPost(clientSecret);
      }

      case OAuthTokenEndpointAuthMethod.ClientSecretBasic: {
        return ClientSecretBasic(clientSecret);
      }

      default: {

View on GitHub (pinned to e55ac299a4)