unoplatform/uno · error · InvalidOperationException

Failed load the animation

Error message

Failed load the animation

What it means

A catch-all wrapper inside the OnJsonChanged callback that swallows any exception raised while loading/re-parsing the animation (including error 20) and re-throws it as a new InvalidOperationException with the original as InnerException. Note the message has a typo ('Failed load the animation'). This wraps the whole TryCreate/SetAnimation/Play block, so it also fires on failures inside SetAnimation or Play, not only parse failures.

Source

Thrown at src/AddIns/Uno.UI.Lottie/LottieVisualSource.Skottie.cs:140

											this.Log().Debug($"Version: {animation.Version} Duration: {animation.Duration} Fps:{animation.Fps} InPoint: {animation.InPoint} OutPoint: {animation.OutPoint}");
										}
									}
									else
									{
										throw new InvalidOperationException("Failed to load animation.");
									}

									SetAnimation(animation);

									if (_playState != null)
									{
										var (fromProgress, toProgress, looped) = _playState;
										Play(fromProgress, toProgress, looped);
									}
								}
								catch (Exception ex)
								{
									throw new InvalidOperationException("Failed load the animation", ex);
								}
							}
						}
						else
						{
							throw new NotSupportedException($"Failed to load animation: {sourceUri}");
						}

						// Force layout to recalculate
						player.InvalidateMeasure();
						player.InvalidateArrange();

						if (_playState != null)
						{
							var (fromProgress, toProgress, looped) = _playState;
							Play(fromProgress, toProgress, looped);
						}
						else if (player.AutoPlay)

View on GitHub (pinned to 0418340488)

Solutions

  1. Inspect ex.InnerException (and InnerException.InnerException for error 20) — the true failure is nested, not the 'Failed load the animation' message.
  2. Fix the underlying cause identified via InnerException (parse error, null state, etc.).
  3. Enable Debug logging on the LottieVisualSource to capture the Version/Duration/Fps trace that prints only on successful parse, confirming whether TryCreate ever succeeded.
  4. If updating this add-in source, consider preserving the original exception type instead of blanket-wrapping into InvalidOperationException.

Example fix

// before — blanket wrap hides root cause
catch (Exception ex)
{
    throw new InvalidOperationException("Failed load the animation", ex);
}

// after — let parse failures surface their real type, only wrap unknowns
catch (InvalidOperationException) { throw; }
catch (Exception ex)
{
    throw new InvalidOperationException("Failed load the animation", ex);
}
Defensive patterns

Strategy: try-catch

Try / catch

try { player.Source = lottieSource; }
catch (InvalidOperationException ex) when (ex.Message.StartsWith("Failed load the animation"))
{
    // real cause is in ex.InnerException (possibly nested)
    Log.Error(ex.InnerException, "Lottie load failed");
    ShowFallback();
}

Prevention

When it happens

Trigger: Any exception thrown within the OnJsonChanged body: Skottie.Animation.TryCreate returning false (re-wrapped error 20), a null-reference in SetAnimation, or Play throwing because the play state is invalid. The catch wraps every statement from stream creation through Play().

Common situations: Hot-reload or cache-invalidation flow delivering a malformed updatedJson payload; calling Play before the animation finished initializing; a SkiaSharp version mismatch where Animation members behave unexpectedly. Because it re-wraps, the real root cause is in InnerException and easy to miss.

Related errors


AI-assisted analysis of unoplatform/uno@0418340488 (2026-08-13). Data as JSON: /api/errors/96bba9d84a05727d. Report an issue: GitHub.