immich-app/immich · error · UnauthorizedException

Authentication required

Error message

Authentication required

What it means

Thrown by validate() when a request carries neither a session/access token nor an API key, so the caller cannot be identified. The service rejects with 401 because no authentication credential exists at all.

Solutions

  1. Add the x-api-key header (or apikey query param) with a valid API key for machine access
  2. Log in via web/mobile to obtain a session cookie for user flows
  3. Ensure the reverse proxy forwards Cookie and x-api-key headers
  4. Check that your HTTP client actually attaches the configured credentials

Example fix

// before
fetch('/api/albums')
// after
fetch('/api/albums', { headers: { 'x-api-key': process.env.IMMICH_API_KEY } })
Defensive patterns

Strategy: validation

Validate before calling

if (!apiKey && !sessionCookie) throw new Error('Provide x-api-key header or a logged-in session before calling the API');

Type guard

const isAuthed = (r: { headers: Record<string, string> }) => Boolean(r.headers['x-api-key'] || r.headers['cookie']);

Try / catch

try { await api.request() } catch (e) { if (e.status === 401 && /Authentication required/.test(e.message)) { /* attach credentials and retry once */ } throw e; }

Prevention

When it happens

Trigger: validate() sees no accessToken in cookies/headers and no apiKey header or query param; both credential paths are undefined.

Common situations: Omitting the x-api-key header in scripts/curl; expired or cleared Immich session cookie; reverse proxy stripping Cookie/x-api-key headers; machine clients with no credentials configured.

Understand the failure class

Related errors


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

Appendix: source

Thrown at server/src/services/auth.service.ts:267

    const apiKey = (headers[ImmichHeader.ApiKey] || queryParams[ImmichQuery.ApiKey]) as string;

    if (shareKey) {
      return this.validateSharedLinkKey(shareKey);
    }

    if (shareSlug) {
      return this.validateSharedLinkSlug(shareSlug);
    }

    if (session) {
      return this.validateSession(session, headers);
    }

    if (apiKey) {
      return this.validateApiKey(apiKey);
    }

    throw new UnauthorizedException('Authentication required');
  }

  getMobileRedirect(url: string) {
    return `${MOBILE_REDIRECT}?${url.split('?', 2)[1] || ''}`;
  }

  async authorize(dto: OAuthConfigDto) {
    const { oauth } = await this.getConfig({ withCache: false });

    if (!oauth.enabled) {
      throw new BadRequestException('OAuth is not enabled');
    }

    return await this.oauthRepository.authorize(
      oauth,
      this.resolveRedirectUri(oauth, dto.redirectUri),
      dto.state,
      dto.codeChallenge,

View on GitHub (pinned to f48d4b3321)