airbnb/lottie-android · error · IllegalStateException

Unable to parse composition

Error message

Unable to parse composition

What it means

Thrown by LottieAnimationView's DEFAULT_FAILURE_LISTENER when a composition fails to load for any reason that is NOT a network exception. The original Throwable is wrapped in an IllegalStateException, so 'Unable to parse composition' is a wrapper around the real decoding/parsing cause (e.g. JSON syntax error, IllegalStateException from the parser, OutOfMemoryError). Network-classified throwables are instead logged and swallowed.

Source

Thrown at lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java:74

 * {@link #setComposition(LottieComposition)}
 * <p>
 * You can set a default cache strategy with {@link R.attr#lottie_cacheComposition}.
 * <p>
 * You can manually set the progress of the animation with {@link #setProgress(float)} or
 * {@link R.attr#lottie_progress}
 *
 * @see <a href="http://airbnb.io/lottie">Full Documentation</a>
 */
@SuppressWarnings({"WeakerAccess", "unused"}) public class LottieAnimationView extends AppCompatImageView {

  private static final String TAG = LottieAnimationView.class.getSimpleName();
  private static final LottieListener<Throwable> DEFAULT_FAILURE_LISTENER = throwable -> {
    // By default, fail silently for network errors.
    if (Utils.isNetworkException(throwable)) {
      Logger.warning("Unable to load composition.", throwable);
      return;
    }
    throw new IllegalStateException("Unable to parse composition", throwable);
  };

  private final LottieListener<LottieComposition> loadedListener = new WeakSuccessListener(this);

  private static class WeakSuccessListener implements LottieListener<LottieComposition> {

    private final WeakReference<LottieAnimationView> targetReference;

    public WeakSuccessListener(LottieAnimationView target) {
      this.targetReference = new WeakReference<>(target);
    }

    @Override public void onResult(LottieComposition result) {
      LottieAnimationView targetView = targetReference.get();
      if (targetView == null) {
        return;
      }
      targetView.setComposition(result);

View on GitHub (pinned to 05ea92e903)

Solutions

  1. Open the file in the LottieFiles previewer or a JSON validator to confirm it is a valid Lottie/Bodymovin export (root object has "v", "fr", "layers", etc.).
  2. Check the wrapped Throwable message printed below this exception - it names the actual parser/decoding failure and usually pinpoints the bad field.
  3. Verify the res/raw or assets file is the Lottie JSON and not a gzipped binary or an unrelated JSON config.
  4. Align the lottie-android version with the feature set of your file; upgrade the dependency if the file uses newer features.
  5. Replace the default listener via LottieAnimationView#setFailureListener(LottieListener) or load through LottieCompositionFactory (fromRawRes/fromAsset/fromUrl) whose failure callback exposes the real Throwable instead of rethrowing.

Example fix

// before
lottieAnimationView.setAnimation(R.raw.broken);
lottieAnimationView.playAnimation();

// after - inspect the real cause instead of hitting the default rethrower
LottieCompositionFactory.fromRawRes(this, R.raw.broken)
    .addListener(c -> { lottieAnimationView.setComposition(c); lottieAnimationView.playAnimation(); })
    .addFailureListener(t -> Log.e(TAG, "Real cause:", t));
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate the file is plausible Lottie JSON before handing it to the view
boolean looksLikeLottie(String json) {
  try {
    JSONObject o = new JSONObject(json);
    return o.has("v") && (o.has("layers") || o.has("assets") || o.has("fr"));
  } catch (Exception e) { return false; }
}
// usage: read the asset, check, then setAnimation only if true

Try / catch

// Replace the rethrowing default listener with one that inspects the real cause
lottieAnimationView.setFailureListener(throwable -> {
  Log.e(TAG, "Lottie load failed", throwable);
});
// Or load via the factory to get the unwrapped Throwable:
LottieCompositionFactory.fromAsset(this, "anim.json")
    .addFailureListener(t -> Log.e(TAG, "real cause", t));

Prevention

When it happens

Trigger: Calling setAnimation(int rawRes), setAnimation(String fileName), or setAnimationFromUrl(String) with a malformed file; a file that is valid JSON but not a Bodymovin/Lottie schema; a truncated or zero-byte asset; a network response whose body fails to parse but whose error is not classified by Utils.isNetworkException (e.g. HTTP 200 with HTML/gzip-corrupt body, or a NumberFormatException during frame parsing).

Common situations: Pointing lottie_rawRes/lottie_fileName at the wrong asset (a plain config JSON or a wrong-format export), a Lottie file using features unsupported by the installed lottie-android version, a Bodymovin plugin/After Effects version mismatch producing an incompatible schema, a gzip-compressed file loaded where raw JSON is expected, or a file truncated during download.

Understand the failure class

Related errors


AI-assisted analysis of airbnb/lottie-android@05ea92e903 (2026-08-14). Data as JSON: /api/errors/b3c731a697794cc6. Report an issue: GitHub.