{"record":{"id":"8c122f1f3a14a2be","repo":"immich-app/immich","slug":"skipped-transcoding-for-asset-asset-id-missing-metadata-re","errorCode":null,"errorMessage":"Skipped transcoding for asset ${asset.id}: missing metadata; re-run extraction first","messagePattern":"Skipped transcoding for asset (.+?): missing metadata; re-run extraction first","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"server/src/services/media.service.ts","lineNumber":561,"sourceCode":"\n    return JobStatus.Success;\n  }\n\n  @OnJob({ name: JobName.AssetEncodeVideo, queue: QueueName.VideoConversion })\n  async handleVideoConversion({ id }: JobOf<JobName.AssetEncodeVideo>): Promise<JobStatus> {\n    const asset = await this.assetJobRepository.getForVideoConversion(id);\n    if (!asset) {\n      return JobStatus.Failed;\n    }\n\n    const input = asset.originalPath;\n    const output = StorageCore.getEncodedVideoPath(asset);\n    this.storageCore.ensureFolders(output);\n\n    const { videoStream, format } = asset;\n    const audioStream = asset.audioStream ?? undefined;\n    if (!videoStream || !format) {\n      this.logger.warn(`Skipped transcoding for asset ${asset.id}: missing metadata; re-run extraction first`);\n      return JobStatus.Failed;\n    }\n    if (!videoStream.height || !videoStream.width) {\n      this.logger.warn(`Skipped transcoding for asset ${asset.id}: no video dimensions`);\n      return JobStatus.Failed;\n    }\n\n    let { ffmpeg } = await this.getConfig({ withCache: true });\n    const target = this.getTranscodeTarget(ffmpeg, videoStream, audioStream);\n    if (target === TranscodeTarget.None && !this.isRemuxRequired(ffmpeg, format)) {\n      const encodedVideo = getAssetFile(asset.files, AssetFileType.EncodedVideo, { isEdited: false });\n      if (encodedVideo) {\n        this.logger.log(`Transcoded video exists for asset ${asset.id}, but is no longer required. Deleting...`);\n        await this.jobRepository.queue({ name: JobName.FileDelete, data: { files: [encodedVideo.path] } });\n        await this.assetRepository.deleteFiles([encodedVideo]);\n      } else {\n        this.logger.verbose(`Asset ${asset.id} does not require transcoding based on current policy, skipping`);\n      }","sourceCodeStart":543,"sourceCodeEnd":579,"githubUrl":"https://github.com/immich-app/immich/blob/e55ac299a4ec7cb372e35dbf2c6c05ee9ce77f6c/server/src/services/media.service.ts#L543-L579","documentation":"Video transcoding requires the asset's video stream and container format metadata, which are populated by a prior metadata-extraction step. If either is missing, the handler logs this warning and returns JobStatus.Failed — it cannot build a valid ffmpeg command without stream/format info.","triggerScenarios":"handleVideoConversion runs on an asset whose videoStream or format is null because metadata extraction never ran, failed, or predates the schema that stores this metadata.","commonSituations":"Transcode job executed before Video Metadata job completed; ffprobe failed silently during extraction; assets imported from an older schema lacking stream rows; corrupted video ffprobe cannot parse.","solutions":["Re-run the Video Metadata (metadata extraction) job for the asset, then retry transcoding","Check ffprobe output for the file: the container may be corrupt or unsupported","Verify the metadata queue is ordered before transcoding for new uploads","If many assets hit this after an upgrade, run a full metadata re-extraction"],"exampleFix":"// before\nif (!videoStream || !format) { this.logger.warn(`Skipped transcoding for asset ${asset.id}: missing metadata; re-run extraction first`); return JobStatus.Failed; }\n// after\nif (!videoStream || !format) { this.logger.warn(`Skipped transcoding for asset ${asset.id}: missing metadata; re-run extraction first`); await this.jobRepository.queue({ name: JobName.VideoMetadata, data: { id: asset.id } }); return JobStatus.Failed; }","handlingStrategy":"retry","validationCode":"if (!asset.videoStream || !asset.format) {\n  await jobRepository.queue({ name: JobName.VideoMetadata, data: { id: asset.id } }); // re-extract first\n}","typeGuard":null,"tryCatchPattern":"if (!videoStream || !format) {\n  this.logger.warn(`Skipped transcoding for asset ${asset.id}: missing metadata`);\n  await this.jobRepository.queue({ name: JobName.VideoMetadata, data: { id: asset.id } });\n  return JobStatus.Failed;\n}","preventionTips":["Keep the metadata queue ordered before transcoding","Re-run video metadata after server/schema upgrades","Verify ffprobe succeeds on all ingested videos","Alert when transcode failures correlate with null stream rows"],"tags":["video","transcoding","metadata"],"backgroundTag":"missing-dependency","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"}