{"record":{"id":"0589e18b6f887bd9","repo":"immich-app/immich","slug":"library-id-not-found","errorCode":null,"errorMessage":"Library ${id} not found","messagePattern":"Library (.+?) not found","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/services/library.service.ts","lineNumber":202,"sourceCode":"      await this.unwatch(id);\n    }\n  }\n\n  async watchAll() {\n    if (!this.lock) {\n      return false;\n    }\n\n    const libraries = await this.libraryRepository.getAll(false);\n    for (const library of libraries) {\n      await this.watch(library.id);\n    }\n  }\n\n  async getStatistics(id: string): Promise<LibraryStatsResponseDto> {\n    const statistics = await this.libraryRepository.getStatistics(id);\n    if (!statistics) {\n      throw new BadRequestException(`Library ${id} not found`);\n    }\n    return statistics;\n  }\n\n  async get(id: string): Promise<LibraryResponseDto> {\n    const library = await this.findOrFail(id);\n    return mapLibrary(library);\n  }\n\n  async getAll(): Promise<LibraryResponseDto[]> {\n    const libraries = await this.libraryRepository.getAll(false);\n    return libraries.map((library) => mapLibrary(library));\n  }\n\n  @OnJob({ name: JobName.LibraryDeleteCheck, queue: QueueName.Library })\n  async handleQueueCleanup(): Promise<JobStatus> {\n    this.logger.log('Checking for any libraries pending deletion...');\n    const pendingDeletions = await this.libraryRepository.getAllDeleted();","sourceCodeStart":184,"sourceCodeEnd":220,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/library.service.ts#L184-L220","documentation":"LibraryService.getStatistics calls libraryRepository.getStatistics(id); if the repository returns no statistics row (typically because no library with that id exists), it throws 400 BadRequestException 'Library {id} not found'. Note this uses the statistics lookup as an existence proxy, so the error fires for a missing library rather than via findOrFail.","triggerScenarios":"GET /libraries/{id}/statistics where id does not correspond to any library row. Caused by a deleted library, a wrong UUID copied from the UI, or a stale client reference.","commonSituations":"Library was deleted but the admin UI still had the link open; typo in id; script iterating over cached ids after a re-import.","solutions":["List libraries via GET /libraries and use a valid id from the response.","If the library was deleted, stop referencing it.","Confirm the id format is a valid UUID and matches an existing library."],"exampleFix":"// before\napi.library.getStatistics('not-a-real-id');\n// after\nconst libs = await api.library.getAll();\nawait api.library.getStatistics(libs[0].id);","handlingStrategy":"validation","validationCode":"const libs = await api.libraryApi.getAll();\nconst exists = libs.some((l) => l.id === id);\nif (!exists) throw new Error(`Library ${id} does not exist`);\nawait api.libraryApi.getStatistics(id);","typeGuard":"const libraryExists = (id: string, libraries: { id: string }[]) =>\n  libraries.some((l) => l.id === id);","tryCatchPattern":"try {\n  return await api.libraryApi.getStatistics(id);\n} catch (e) {\n  if (e.status === 400 && /not found/.test(e.message)) {\n    // refresh library list, drop stale id\n  } else throw e;\n}","preventionTips":["Cache the library list and validate ids against it before sub-resource calls.","Handle library deletions in the UI by invalidating cached ids.","Use GET /libraries as the source of truth for valid ids."],"tags":["api","library","validation","not-found","bad-request","immich","nestjs"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}