immich-app/immich · error · StateError
Failed to load animated codec for local asset
Error message
Failed to load animated codec for local asset ${key.id} What it means
local_image_provider's animated variant loads a codec for a local asset; when loadCodecRequest returns null (no decodable animated frame could be produced for the asset id), a StateError is thrown because an animated codec is required to emit.
Solutions
- Fall back to the static (non-animated) decode path when the animated codec is null
- Pre-check the asset's mime/format and route unsupported animated formats to the static provider
- Skip or placeholder the asset instead of throwing when it is unreadable
Example fix
// before
final codec = await loadCodecRequest(originalRequest, isFinal: true);
if (codec == null) throw StateError('Failed to load animated codec ...');
// after
final codec = await loadCodecRequest(originalRequest, isFinal: true) ??
await loadStaticCodec(originalRequest); // graceful fallback Defensive patterns
Strategy: fallback
Validate before calling
// Dart: confirm the asset is still present and decodable final exists = await fileExistsForAsset(key.id); if (!exists) return placeholderCodec();
Try / catch
try {
yield* animatedFrames(key);
} on StateError catch (e) {
log('animated decode failed: $e');
yield* staticFrames(key); // non-animated fallback
} Prevention
- Verify asset existence before decode
- Route unsupported animated formats to the static provider
- Handle deletion/move of assets during image streaming
- Keep image decoder plugins up to date
When it happens
Trigger: ImageStreamDecorator-style request for a local asset whose animated codec cannot be loaded: unsupported/unknown animated format, asset file unreadable, or decoder returned no frames for key.id.
Common situations: Displaying a corrupted or exotic animated asset (rare GIF/HEVC variants); asset deleted or moved after it was picked; plugin/decoder limitation on specific formats.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Failed to get sync stream: $errorBody
- Failed to load animated codec for asset
- No file found for the video
- User must be logged in to access archive
- User must be logged in to access archive
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/15fbead49322e4aa.
Report an issue: GitHub.
Appendix: source
Thrown at mobile/lib/presentation/widgets/images/local_image_provider.dart:193
final previewRequest = request = LocalImageRequest(
localId: key.id,
size: _previewTarget(devicePixelRatio, false),
assetType: key.assetType,
);
yield* loadRequest(previewRequest, decode, isFinal: false);
if (isCancelled) {
return;
}
// always try original for animated, since previews don't support animation
final originalRequest = request = LocalImageRequest(localId: key.id, size: Size.zero, assetType: key.assetType);
final codec = await loadCodecRequest(originalRequest, isFinal: true);
if (codec == null) {
if (isCancelled) {
return;
}
throw StateError('Failed to load animated codec for local asset ${key.id}');
}
yield codec;
}
@override
bool operator ==(Object other) {
if (identical(this, other)) {
return true;
}
if (other is LocalFullImageProvider) {
return id == other.id &&
size == other.size &&
isAnimated == other.isAnimated &&
width == other.width &&
height == other.height &&
checksum == other.checksum;
}
return false;View on GitHub (pinned to e55ac299a4)