{"record":{"id":"c67a4fcb828456a9","repo":"immich-app/immich","slug":"invalid-api-key","errorCode":null,"errorMessage":"Invalid API key","messagePattern":"Invalid API key","errorType":"exception","errorClass":"UnauthorizedException","httpStatus":401,"severity":"error","filePath":"server/src/services/auth.service.ts","lineNumber":537,"sourceCode":"  }\n\n  private isValidSharedLink(\n    sharedLink?: AuthSharedLink & { user: AuthUser | null },\n  ): sharedLink is AuthSharedLink & { user: AuthUser } {\n    return !!sharedLink?.user && (!sharedLink.expiresAt || new Date(sharedLink.expiresAt) > new Date());\n  }\n\n  private async validateApiKey(key: string): Promise<AuthDto> {\n    const hashed = this.cryptoRepository.hashSha256(key);\n    const apiKey = await this.apiKeyRepository.getKey(hashed);\n    if (apiKey?.user) {\n      return {\n        user: apiKey.user,\n        apiKey,\n      };\n    }\n\n    throw new UnauthorizedException('Invalid API key');\n  }\n\n  private validateSecret(inputSecret: string, existingHash?: string | null): boolean {\n    if (!existingHash) {\n      return false;\n    }\n\n    return this.cryptoRepository.compareBcrypt(inputSecret, existingHash);\n  }\n\n  private async validateSession(token: string, headers: IncomingHttpHeaders): Promise<AuthDto> {\n    const hashed = this.cryptoRepository.hashSha256(token);\n    const session = await this.sessionRepository.getByToken(hashed);\n    if (session?.user) {\n      const { appVersion, deviceOS, deviceType } = getUserAgentDetails(headers);\n      const now = DateTime.now();\n      const updatedAt = DateTime.fromJSDate(session.updatedAt);\n      const diff = now.diff(updatedAt, ['hours']);","sourceCodeStart":519,"sourceCodeEnd":555,"githubUrl":"https://github.com/immich-app/immich/blob/f48d4b332127ad365ba256108799ca8f571d2dd5/server/src/services/auth.service.ts#L519-L555","documentation":"API-key authentication hashes the presented key and compares it (via validateSecret) against the stored hash. If no API key record matches the provided secret, an UnauthorizedException('Invalid API key') is thrown.","triggerScenarios":"Request with x-api-key header whose value does not match any stored key hash, or validate() routed to API-key auth with an empty/malformed key.","commonSituations":"Key rotated/revoked server-side while client still uses the old value; key truncated or whitespace-padded in env config; copying the key name/label instead of the secret; regenerating the key without updating integrations.","solutions":["Regenerate or re-copy the API key from user settings and update the client","Ensure the header value is the secret key with no surrounding whitespace or quotes","Confirm the key is still active and was not deleted/rotated","Verify the client sends the key on the expected header for this endpoint"],"exampleFix":"// before\nconst key = process.env.IMMICH_API_KEY.trim().split(' ')[0];\n// after\nconst key = process.env.IMMICH_API_KEY;","handlingStrategy":"validation","validationCode":"if (!/^[A-Za-z0-9]{20,}$/.test(apiKey)) throw new Error('malformed api key');","typeGuard":null,"tryCatchPattern":"catch (e) { if (e.status === 401 && e.message === 'Invalid API key') { /* refresh key from settings */ } }","preventionTips":["Store keys in env without quotes/whitespace","Rotate keys in lockstep with clients","Send the secret, not the key label"],"tags":["api-key","authentication","unauthorized"],"backgroundTag":"invalid-api-key","analyzedSha":"f48d4b332127ad365ba256108799ca8f571d2dd5","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}