{"record":{"id":"5ef5e836b39dee7a","repo":"immich-app/immich","slug":"api-key-not-found","errorCode":null,"errorMessage":"API Key not found","messagePattern":"API Key not found","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/services/api-key.service.ts","lineNumber":32,"sourceCode":"\n    if (auth.apiKey && !isGranted({ requested: dto.permissions, current: auth.apiKey.permissions })) {\n      throw new BadRequestException('Cannot grant permissions you do not have');\n    }\n\n    const entity = await this.apiKeyRepository.create({\n      key: hashed,\n      name: dto.name || 'API Key',\n      userId: auth.user.id,\n      permissions: dto.permissions,\n    });\n\n    return { secret: token, apiKey: this.map(entity) };\n  }\n\n  async update(auth: AuthDto, id: string, dto: ApiKeyUpdateDto): Promise<ApiKeyResponseDto> {\n    const exists = await this.apiKeyRepository.getById(auth.user.id, id);\n    if (!exists) {\n      throw new BadRequestException('API Key not found');\n    }\n\n    if (\n      auth.apiKey &&\n      dto.permissions &&\n      !isGranted({ requested: dto.permissions, current: auth.apiKey.permissions })\n    ) {\n      throw new BadRequestException('Cannot grant permissions you do not have');\n    }\n\n    const key = await this.apiKeyRepository.update(auth.user.id, id, { name: dto.name, permissions: dto.permissions });\n\n    return this.map(key);\n  }\n\n  async delete(auth: AuthDto, id: string): Promise<void> {\n    const exists = await this.apiKeyRepository.getById(auth.user.id, id);\n    if (!exists) {","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/api-key.service.ts#L14-L50","documentation":"Thrown by ApiKeyService.update when apiKeyRepository.getById(userId, id) returns null for the authenticated user. The lookup is scoped to auth.user.id, so a missing row means the id does not exist, belongs to a different user, or was already deleted. Returned as BadRequestException (HTTP 400) rather than NotFound, so callers must not assume 404 semantics.","triggerScenarios":"PUT /api-keys/:id where :id is not a valid API key id for the authenticated user; calling update twice where the second call follows a successful delete; passing a UUID from a different user's account.","commonSituations":"Stale client state after a key was deleted in another session/tab; copy-paste of the wrong id; frontend cache that retains a key after removal; concurrent admin actions.","solutions":["Verify the key still exists (GET /api-keys) before issuing the update, or refresh the list after any delete","Treat a 400 with this message as 'not found' and remove the key from local state","Ensure the id belongs to the authenticated user — keys are user-scoped","Guard against double-submit / race with a UI lock after the first action"],"exampleFix":"// before\nawait sdk.updateApiKey(maybeStaleId, { name: 'renamed' });\n// after\nconst keys = await sdk.getAllApiKeys();\nif (!keys.some(k => k.id === maybeStaleId)) { /* drop from UI */ return; }\nawait sdk.updateApiKey(maybeStaleId, { name: 'renamed' });","handlingStrategy":"validation","validationCode":"// Confirm the key still exists for the user before updating\nconst keys = await sdk.getAllApiKeys();\nif (!keys.some(k => k.id === id)) {\n  throw new Error(`API key ${id} not found for this user`);\n}\nawait sdk.updateApiKey(id, dto);","typeGuard":"function isApiKeyRow(x: unknown): x is { id: string; name: string; permissions: string[] } {\n  return typeof x === 'object' && x !== null && typeof (x as any).id === 'string';\n}","tryCatchPattern":"try {\n  await sdk.updateApiKey(id, dto);\n} catch (e) {\n  if (e instanceof HttpError && e.status === 400 && /API Key not found/.test(e.message)) {\n    ui.removeKey(id); // treat as already-deleted\n    return;\n  }\n  throw e;\n}","preventionTips":["Refresh the key list after any create/delete before issuing further edits","Make update handlers idempotent against 'not found' outcomes","Keys are user-scoped — never reuse ids observed in another account"],"tags":["api-key","not-found","crud","nestjs","concurrency"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}