{"record":{"id":"26a53c6a6ae7e951","repo":"immich-app/immich","slug":"asset-not-found","errorCode":null,"errorMessage":"Asset not found","messagePattern":"Asset not found","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/services/asset.service.ts","lineNumber":75,"sourceCode":"\n    const stats = await this.assetRepository.getStatistics(auth.user.id, dto);\n    return mapStats(stats);\n  }\n\n  async get(auth: AuthDto, id: string): Promise<AssetResponseDto | SanitizedAssetResponseDto> {\n    await this.requireAccess({ auth, permission: Permission.AssetRead, ids: [id] });\n\n    const asset = await this.assetRepository.getById(id, {\n      exifInfo: true,\n      owner: true,\n      faces: { person: true },\n      stack: { assets: true },\n      edits: true,\n      tags: true,\n    });\n\n    if (!asset) {\n      throw new BadRequestException('Asset not found');\n    }\n\n    if (auth.sharedLink && !auth.sharedLink.showExif) {\n      return mapAsset(asset, { stripMetadata: true, withStack: true, auth });\n    }\n\n    const data = mapAsset(asset, { withStack: true, auth });\n\n    if (auth.sharedLink) {\n      delete data.owner;\n    }\n\n    if (data.ownerId !== auth.user.id || auth.sharedLink) {\n      data.people = [];\n    }\n\n    return data;\n  }","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/asset.service.ts#L57-L93","documentation":"Thrown by AssetService.getOne when assetRepository.getById returns null after requireAccess already confirmed the user has AssetRead permission. Because the access check passed, the most likely cause is a race: the asset was deleted between the access check and the detail fetch. Returned as BadRequestException (HTTP 400), not 404.","triggerScenarios":"GET /assets/:id that passes access control but finds no row on the detailed getById (with exifInfo, owner, faces, stack, edits, tags relations). Concurrent delete is the classic trigger.","commonSituations":"Timeline still showing an asset after it was deleted from another client; long-lived detail view while a cleanup job runs; optimistic UI not refreshing after delete.","solutions":["Refresh the asset list after any delete to remove stale ids","Treat 400 'Asset not found' as removal and drop the asset from local state","Avoid concurrent delete+view of the same asset"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  return await sdk.getAsset(id);\n} catch (e) {\n  if (e instanceof HttpError && e.status === 400 && /Asset not found/.test(e.message)) {\n    ui.removeAsset(id); // stale reference, drop it\n    return null;\n  }\n  throw e;\n}","preventionTips":["Refresh the asset list after any delete to remove stale ids","Treat 'Asset not found' as removal from local state","Avoid concurrent delete + detail view of the same asset"],"tags":["asset","not-found","race-condition","crud","nestjs"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}