{"record":{"id":"aa3e246e56f22a64","repo":"immich-app/immich","slug":"could-not-find-asset-item-data-id-after-generating","errorCode":null,"errorMessage":"Could not find asset ${item.data.id} after generating thumbnails","messagePattern":"Could not find asset (.+?) after generating thumbnails","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"server/src/services/job.service.ts","lineNumber":174,"sourceCode":"              width: asset.width,\n              height: asset.height,\n              isEdited: asset.isEdited,\n            },\n            edit: edits,\n          });\n        }\n\n        break;\n      }\n\n      case JobName.AssetGenerateThumbnails: {\n        if (!item.data.notify && item.data.source !== 'upload') {\n          break;\n        }\n\n        const [asset] = await this.assetRepository.getByIdsWithAllRelationsButStacks([item.data.id]);\n        if (!asset) {\n          this.logger.warn(`Could not find asset ${item.data.id} after generating thumbnails`);\n          break;\n        }\n\n        const jobs: JobItem[] = [\n          { name: JobName.SmartSearch, data: item.data },\n          { name: JobName.AssetDetectFaces, data: item.data },\n          { name: JobName.Ocr, data: item.data },\n        ];\n\n        if (asset.type === AssetType.Video) {\n          jobs.push({ name: JobName.AssetEncodeVideo, data: item.data });\n        }\n\n        await this.jobRepository.queueAll(jobs);\n        if (asset.visibility === AssetVisibility.Timeline || asset.visibility === AssetVisibility.Archive) {\n          this.websocketRepository.clientSend('on_upload_success', asset.ownerId, mapAsset(asset));\n          if (asset.exifInfo) {\n            const exif = asset.exifInfo;","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/server/src/services/job.service.ts#L156-L192","documentation":"After thumbnail generation completes, the job handler re-fetches the asset (with all relations) to enqueue follow-up jobs like Smart Search and face detection. This warning is logged when the asset no longer exists in the database. The job intentionally breaks instead of throwing, since the follow-up work is meaningless for a deleted asset.","triggerScenarios":"The asset row was deleted (e.g. by user deletion or a trash/truncate job) between job enqueue and job completion, so getByIdsWithAllRelationsButStacks returns an empty array.","commonSituations":"User deletes an asset while its thumbnail job is queued; bulk deletion running concurrently with thumbnail generation; database restored/rolled back leaving orphaned job items.","solutions":["Confirm the asset id exists: SELECT * FROM asset WHERE id = '<id>'; if absent, the warning is expected and safe to ignore","Avoid deleting assets while large thumbnail jobs are queued; pause deletion or drain the queue first","If warnings appear en masse for stale queue entries, clear orphaned queue jobs (flush the thumbnail queue)","If assets should exist, check DB replication/restore state and repository wiring"],"exampleFix":"// before\nconst [asset] = await this.assetRepository.getByIdsWithAllRelationsButStacks([item.data.id]);\nif (!asset) { this.logger.warn(`Could not find asset ${item.data.id} after generating thumbnails`); break; }\n// after\nconst [asset] = await this.assetRepository.getByIdsWithAllRelationsButStacks([item.data.id]);\nif (!asset) { this.logger.verbose(`Asset ${item.data.id} deleted before post-thumbnail processing; skipping`); break; }","handlingStrategy":"type-guard","validationCode":"const assets = await assetRepository.getByIdsWithAllRelationsButStacks([id]);\nif (assets.length === 0) return; // asset deleted; skip follow-up jobs","typeGuard":"function isAsset(a: Asset | undefined): a is Asset { return a !== undefined; }","tryCatchPattern":"const [asset] = await this.assetRepository.getByIdsWithAllRelationsButStacks([item.data.id]);\nif (!asset) { this.logger.warn(`Could not find asset ${item.data.id}`); break; }","preventionTips":["Drain or flush job queues before bulk deletions","Treat post-job asset lookups as possibly-empty (undefined)","Use verbose logs for expected deletions, warn only for anomalies","Monitor for mass occurrences — indicates stale queue entries"],"tags":["jobs","database","race-condition"],"backgroundTag":"entity-not-found","analyzedSha":"e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}