{"record":{"id":"b3c731a697794cc6","repo":"airbnb/lottie-android","slug":"unable-to-parse-composition","errorCode":null,"errorMessage":"Unable to parse composition","messagePattern":"Unable to parse composition","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java","lineNumber":74,"sourceCode":" * {@link #setComposition(LottieComposition)}\n * <p>\n * You can set a default cache strategy with {@link R.attr#lottie_cacheComposition}.\n * <p>\n * You can manually set the progress of the animation with {@link #setProgress(float)} or\n * {@link R.attr#lottie_progress}\n *\n * @see <a href=\"http://airbnb.io/lottie\">Full Documentation</a>\n */\n@SuppressWarnings({\"WeakerAccess\", \"unused\"}) public class LottieAnimationView extends AppCompatImageView {\n\n  private static final String TAG = LottieAnimationView.class.getSimpleName();\n  private static final LottieListener<Throwable> DEFAULT_FAILURE_LISTENER = throwable -> {\n    // By default, fail silently for network errors.\n    if (Utils.isNetworkException(throwable)) {\n      Logger.warning(\"Unable to load composition.\", throwable);\n      return;\n    }\n    throw new IllegalStateException(\"Unable to parse composition\", throwable);\n  };\n\n  private final LottieListener<LottieComposition> loadedListener = new WeakSuccessListener(this);\n\n  private static class WeakSuccessListener implements LottieListener<LottieComposition> {\n\n    private final WeakReference<LottieAnimationView> targetReference;\n\n    public WeakSuccessListener(LottieAnimationView target) {\n      this.targetReference = new WeakReference<>(target);\n    }\n\n    @Override public void onResult(LottieComposition result) {\n      LottieAnimationView targetView = targetReference.get();\n      if (targetView == null) {\n        return;\n      }\n      targetView.setComposition(result);","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/airbnb/lottie-android/blob/05ea92e90381eb8a8ae06855ea2b74f322bebbec/lottie/src/main/java/com/airbnb/lottie/LottieAnimationView.java#L56-L92","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\nlottieAnimationView.setAnimation(R.raw.broken);\nlottieAnimationView.playAnimation();\n\n// after - inspect the real cause instead of hitting the default rethrower\nLottieCompositionFactory.fromRawRes(this, R.raw.broken)\n    .addListener(c -> { lottieAnimationView.setComposition(c); lottieAnimationView.playAnimation(); })\n    .addFailureListener(t -> Log.e(TAG, \"Real cause:\", t));","handlingStrategy":"try-catch","validationCode":"// Validate the file is plausible Lottie JSON before handing it to the view\nboolean looksLikeLottie(String json) {\n  try {\n    JSONObject o = new JSONObject(json);\n    return o.has(\"v\") && (o.has(\"layers\") || o.has(\"assets\") || o.has(\"fr\"));\n  } catch (Exception e) { return false; }\n}\n// usage: read the asset, check, then setAnimation only if true","typeGuard":null,"tryCatchPattern":"// Replace the rethrowing default listener with one that inspects the real cause\nlottieAnimationView.setFailureListener(throwable -> {\n  Log.e(TAG, \"Lottie load failed\", throwable);\n});\n// Or load via the factory to get the unwrapped Throwable:\nLottieCompositionFactory.fromAsset(this, \"anim.json\")\n    .addFailureListener(t -> Log.e(TAG, \"real cause\", t));","preventionTips":["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."],"tags":["parsing","composition","json","asset-loading"],"backgroundTag":null,"analyzedSha":"05ea92e90381eb8a8ae06855ea2b74f322bebbec","analyzedAt":"2026-08-14T00:51:35.636Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}