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
- 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.).
- Check the wrapped Throwable message printed below this exception - it names the actual parser/decoding failure and usually pinpoints the bad field.
- Verify the res/raw or assets file is the Lottie JSON and not a gzipped binary or an unrelated JSON config.
- Align the lottie-android version with the feature set of your file; upgrade the dependency if the file uses newer features.
- 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
- Never ship a lottie_rawRes/lottie_fileName without previewing the file in the LottieFiles player.
- Pin a lottie-android version known to support your file's schema.
- Use LottieCompositionFactory in debug builds to surface the real parsing Throwable.
- Add a unit test that parses each shipped asset with LottieCompositionFactory to catch regressions.
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
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Cannot find marker with name {}.
- Missing values for keyframe.
- Missing values for keyframe.
- Missing values for keyframe.
- Missing values for keyframe.
AI-assisted analysis of airbnb/lottie-android@05ea92e903 (2026-08-14).
Data as JSON: /api/errors/b3c731a697794cc6.
Report an issue: GitHub.