{"record":{"id":"a1952075b5123410","repo":"immich-app/immich","slug":"authentication-required","errorCode":null,"errorMessage":"Authentication required","messagePattern":"Authentication required","errorType":"http","errorClass":"UnauthorizedException","httpStatus":401,"severity":"critical","filePath":"server/src/services/auth.service.ts","lineNumber":263,"sourceCode":"    const apiKey = (headers[ImmichHeader.ApiKey] || queryParams[ImmichQuery.ApiKey]) as string;\n\n    if (shareKey) {\n      return this.validateSharedLinkKey(shareKey);\n    }\n\n    if (shareSlug) {\n      return this.validateSharedLinkSlug(shareSlug);\n    }\n\n    if (session) {\n      return this.validateSession(session, headers);\n    }\n\n    if (apiKey) {\n      return this.validateApiKey(apiKey);\n    }\n\n    throw new UnauthorizedException('Authentication required');\n  }\n\n  getMobileRedirect(url: string) {\n    return `${MOBILE_REDIRECT}?${url.split('?', 2)[1] || ''}`;\n  }\n\n  async authorize(dto: OAuthConfigDto) {\n    const { oauth } = await this.getConfig({ withCache: false });\n\n    if (!oauth.enabled) {\n      throw new BadRequestException('OAuth is not enabled');\n    }\n\n    return await this.oauthRepository.authorize(\n      oauth,\n      this.resolveRedirectUri(oauth, dto.redirectUri),\n      dto.state,\n      dto.codeChallenge,","sourceCodeStart":245,"sourceCodeEnd":281,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/auth.service.ts#L245-L281","documentation":"UnauthorizedException (HTTP 401) thrown by the private validate method when none of the supported credentials are present: no share key, share slug, session token (x-immich-user-token / session-token / ?sessionKey / Bearer / access_token cookie), and no API key. It is the fallback after every credential source has been checked.","triggerScenarios":"Any authenticated route called with no Authorization header, no session cookie, no x-api-key, and no share key/slug. Common with fresh API clients that forgot to set credentials, or after the access-token cookie expired and was cleared.","commonSituations":"Frontend forgot to attach the bearer token; cookie blocked by third-party cookie restrictions; token expired and the refresh path is broken; curl/script missing -H headers; reverse proxy stripping the Authorization header.","solutions":["Attach a valid credential: Authorization: Bearer <accessToken>, x-api-key, session cookie, or share key.","For browser clients, ensure cookies are sent (credentials: 'include') and same-site settings allow them.","If the token expired, call POST /auth/login again to obtain a fresh accessToken.","Check the reverse proxy config preserves the Authorization header and Immich-prefixed headers."],"exampleFix":"// before\nawait axios.get('/albums');\n\n// after\nconst { data } = await axios.post('/auth/login', { email, password });\naxios.defaults.headers.Authorization = `Bearer ${data.accessToken}`;\nawait axios.get('/albums');","handlingStrategy":"validation","validationCode":"function hasCredentials(headers: Record<string, string>): boolean {\n  return Boolean(\n    headers.Authorization ||\n    headers['x-immich-user-token'] ||\n    headers['x-immich-session-token'] ||\n    headers['x-api-key'] ||\n    headers['x-immich-share-key'] ||\n    headers.cookie,\n  );\n}","typeGuard":"function hasAuthHeader(headers: Record<string, unknown>): headers is Record<string, string> & { Authorization: string } {\n  return typeof headers.Authorization === 'string' && headers.Authorization.length > 0;\n}","tryCatchPattern":"try {\n  await axios.get('/albums', { headers: auth() });\n} catch (e) {\n  if (e.response?.status === 401) {\n    const fresh = (await axios.post('/auth/login', creds)).data.accessToken;\n    axios.defaults.headers.Authorization = `Bearer ${fresh}`;\n    await axios.get('/albums');\n  } else throw e;\n}","preventionTips":["Always set Authorization (or a cookie) on authenticated requests.","Use an axios/fetch interceptor to refresh on 401.","Verify the reverse proxy forwards the Authorization header."],"tags":["auth","unauthorized","nestjs","immich"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}