immich-app/immich · error · StateError

Failed to load animated codec for asset

Error message

Failed to load animated codec for asset ${key.assetId}

What it means

The remote image provider attempted to decode an animated asset (e.g. a Live Photo or animated image) but the codec loader returned null even though the request was not cancelled. The provider throws a StateError because it cannot produce any frames to yield. It indicates a decode/codec-loading failure for that specific asset.

Solutions

  1. Verify a codec factory is registered for the asset's format before the provider is used
  2. Check the asset still exists and is downloadable from the server (hit its URL directly)
  3. Log inside loadCodecRequest to find why it returns null (network, decode, or registration)
  4. Handle null gracefully in the UI by falling back to a static thumbnail instead of throwing

Example fix

// before
if (codec == null) {
  throw StateError('Failed to load animated codec for asset ${key.assetId}');
}
// after
if (codec == null) {
  if (isCancelled) return;
  yield* _fallbackStaticImage(originalRequest); // or emit error state to widget
  return;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before building the provider
final exists = await api.asset.getAssetInfo(key.assetId);
if (!exists.isAnimated) return StaticImageProvider(key);

Type guard

bool isCodecAvailable(CodecRequest req) => codecRegistry.contains(req.mimeType);

Try / catch

try {
  await for (final frame in provider.stream) render(frame);
} on StateError catch (e) {
  if (e.message.contains('animated codec')) showStaticFallback(key.assetId);
  rethrow;
}

Prevention

When it happens

Trigger: loadCodecRequest(originalRequest, isFinal: true) resolves to null while isCancelled is false — e.g. the asset bytes failed to fetch or decode, the codec factory was never registered for the asset's format, or the asset was deleted/changed server-side between request and load.

Common situations: Viewing a Motion Photo / Live Photo whose video or image component is unavailable; a custom codec plugin not registered; stale asset references after server migration; network failure on the final chunk of the asset download.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15). Data as JSON: /api/errors/72fd0cc146f90684. Report an issue: GitHub.

Appendix: source

Thrown at mobile/lib/presentation/widgets/images/remote_image_provider.dart:190

        edited: key.edited,
      ),
    );
    yield* loadRequest(previewRequest, decode, isFinal: false);

    if (isCancelled) {
      return;
    }

    // always try original for animated, since previews don't support animation
    final originalRequest = request = RemoteImageRequest(
      uri: getOriginalUrlForRemoteId(key.assetId, edited: key.edited),
    );
    final codec = await loadCodecRequest(originalRequest, isFinal: true);
    if (codec == null) {
      if (isCancelled) {
        return;
      }
      throw StateError('Failed to load animated codec for asset ${key.assetId}');
    }
    yield codec;
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) {
      return true;
    }
    if (other is RemoteFullImageProvider) {
      return assetId == other.assetId &&
          thumbhash == other.thumbhash &&
          isAnimated == other.isAnimated &&
          edited == other.edited;
    }

    return false;
  }

View on GitHub (pinned to e55ac299a4)