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
- Verify the issuerUrl is reachable from the server: curl <issuerUrl>/.well-known/openid-configuration
- Check DNS/firewall/egress rules and whether IPv6 resolution is failing (AggregateError)
- Set allowInsecureRequests when the issuer is http:// (local/dev only)
- 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
- Run a readiness probe that hits the discovery endpoint before serving traffic
- Enable allowInsecureRequests only for http:// dev issuers
- Check IPv6/IPv4 resolution (AggregateError often means all A/AAAA candidates failed)
- Increase the discovery timeout for slow providers and cache clients after first fetch
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
- Error backchannel logout: token validation failed
- Invalid logout token: it must contain either a sub or a sid…
- Invalid logout token: no claims found
- Received backchannel logout request but OAuth is not enabled
- Unexpected profile response, no `sub`
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)