immich-app/immich · warning
Skipped transcoding for asset
Error message
Skipped transcoding for asset ${asset.id}: no video dimensions What it means
After confirming stream/format metadata exist, transcoding still requires nonzero video dimensions (height and width) to compute scaling and target bitrate. A video stream without dimensions is logged with this warning and the job returns JobStatus.Failed.
Solutions
- Re-run metadata extraction for the asset to repopulate height/width
- Verify the file with ffprobe/ffmpeg -i; replace or re-upload if the video is corrupt
- Upgrade ffmpeg/ffprobe if the codec's dimensions cannot be probed
- Check DB: SELECT width, height FROM video_stream WHERE asset_id = '<id>' to confirm the bad row
Example fix
// before
if (!videoStream.height || !videoStream.width) { this.logger.warn(`Skipped transcoding for asset ${asset.id}: no video dimensions`); return JobStatus.Failed; }
// after
if (!videoStream.height || !videoStream.width) { this.logger.warn(`Skipped transcoding for asset ${asset.id}: no video dimensions`); await this.jobRepository.queue({ name: JobName.VideoMetadata, data: { id: asset.id } }); return JobStatus.Failed; } Defensive patterns
Strategy: retry
Validate before calling
if (videoStream && (!videoStream.height || !videoStream.width)) {
await jobRepository.queue({ name: JobName.VideoMetadata, data: { id: asset.id } }); // re-extract dimensions
} Try / catch
if (!videoStream.height || !videoStream.width) {
this.logger.warn(`Skipped transcoding for asset ${asset.id}: no video dimensions`);
return JobStatus.Failed; // then re-run metadata extraction and retry
} Prevention
- Re-run metadata extraction for assets with 0/null dimensions
- Test problem files with ffprobe to confirm dimensions are extractable
- Keep ffmpeg/ffprobe current for new codecs
- Reject or quarantine uploads whose probe yields no dimensions
When it happens
Trigger: handleVideoConversion reads a videoStream row where height or width is 0/null — typically from a partial or failed ffprobe extraction, or a codec ffprobe could not measure (e.g. exotic codecs, rotated streams with missing side data).
Common situations: ffprobe version issues producing empty dimension fields; corrupted or truncated video files; unusual codecs (e.g. some AV1/ProRes variants) on older ffmpeg builds; rows written before dimensions were extracted.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- Skipped transcoding for asset
- Asset dimensions are not available for editing
- Asset does not have valid dimensions
- Asset metadata is not yet ready for streaming
- Asset not found or asset is not a video
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/330b4971a5e67781.
Report an issue: GitHub.
Appendix: source
Thrown at server/src/services/media.service.ts:565
@OnJob({ name: JobName.AssetEncodeVideo, queue: QueueName.VideoConversion })
async handleVideoConversion({ id }: JobOf<JobName.AssetEncodeVideo>): Promise<JobStatus> {
const asset = await this.assetJobRepository.getForVideoConversion(id);
if (!asset) {
return JobStatus.Failed;
}
const input = asset.originalPath;
const output = StorageCore.getEncodedVideoPath(asset);
this.storageCore.ensureFolders(output);
const { videoStream, format } = asset;
const audioStream = asset.audioStream ?? undefined;
if (!videoStream || !format) {
this.logger.warn(`Skipped transcoding for asset ${asset.id}: missing metadata; re-run extraction first`);
return JobStatus.Failed;
}
if (!videoStream.height || !videoStream.width) {
this.logger.warn(`Skipped transcoding for asset ${asset.id}: no video dimensions`);
return JobStatus.Failed;
}
let { ffmpeg } = await this.getConfig({ withCache: true });
const target = this.getTranscodeTarget(ffmpeg, videoStream, audioStream);
if (target === TranscodeTarget.None && !this.isRemuxRequired(ffmpeg, format)) {
const encodedVideo = getAssetFile(asset.files, AssetFileType.EncodedVideo, { isEdited: false });
if (encodedVideo) {
this.logger.log(`Transcoded video exists for asset ${asset.id}, but is no longer required. Deleting...`);
await this.jobRepository.queue({ name: JobName.FileDelete, data: { files: [encodedVideo.path] } });
await this.assetRepository.deleteFiles([encodedVideo]);
} else {
this.logger.verbose(`Asset ${asset.id} does not require transcoding based on current policy, skipping`);
}
return JobStatus.Skipped;
}
View on GitHub (pinned to e55ac299a4)