airbnb/lottie-android · error · IllegalArgumentException

You must provide a static value in the constructor , call se

Error message

You must provide a static value in the constructor , call setValue, or override getValue.

What it means

LottieRelativeIntegerValueCallback adds an offset to the animation's interpolated integer on every frame. Its default getOffset() returns the inherited `value` field, which starts null unless you supply it. The library throws IllegalArgumentException the first time the keyframe animation evaluates this callback and finds `value == null`, because there is no offset to add. Three initialization paths exist: pass a static offset to the constructor, call setValue(offset), or override getOffset(frameInfo) to compute one per frame.

Source

Thrown at lottie/src/main/java/com/airbnb/lottie/value/LottieRelativeIntegerValueCallback.java:27

@SuppressWarnings({"WeakerAccess", "unused"})
public class LottieRelativeIntegerValueCallback extends LottieValueCallback<Integer> {
  @Override
  public Integer getValue(LottieFrameInfo<Integer> frameInfo) {
    int originalValue = MiscUtils.lerp(
        frameInfo.getStartValue(),
        frameInfo.getEndValue(),
        frameInfo.getInterpolatedKeyframeProgress()
    );
    int newValue = getOffset(frameInfo);
    return originalValue + newValue;
  }

  /**
   * Override this to provide your own offset on every frame.
   */
  public Integer getOffset(LottieFrameInfo<Integer> frameInfo) {
    if (value == null) {
      throw new IllegalArgumentException("You must provide a static value in the constructor " +
          ", call setValue, or override getValue.");
    }
    return value;
  }
}

View on GitHub (pinned to 05ea92e903)

Solutions

  1. Pass a static offset in the constructor: `new LottieRelativeIntegerValueCallback(5)` (or whichever int you need).
  2. If the offset changes at runtime, keep the no-arg constructor but call `callback.setValue(offset)` before or while the animation runs.
  3. If you need a per-frame offset, subclass and override `getOffset(LottieFrameInfo<Integer> frameInfo)` to return a computed int (do NOT override getValue, which is already implemented for you).
  4. Verify the KeyPath/property the callback is bound to actually resolves; a misattached callback still evaluates and hits the null check.

Example fix

// before
LottieRelativeIntegerValueCallback cb = new LottieRelativeIntegerValueCallback();
animationView.addValueCallback(keyPath, LottieProperty.TRANSFORM_OPACITY, cb); // throws on first frame

// after — option A: static offset
LottieRelativeIntegerValueCallback cb = new LottieRelativeIntegerValueCallback(20);

// after — option B: per-frame offset
cb = new LottieRelativeIntegerValueCallback() {
  @Override
  public Integer getOffset(LottieFrameInfo<Integer> frameInfo) {
    return (int) (frameInfo.getOverallProgress() * 100);
  }
};
Defensive patterns

Strategy: validation

Validate before calling

LottieRelativeIntegerValueCallback cb;
// before attaching, ensure it can produce an offset:
boolean ok = false;
try {
  // dry-run the same path the animation will take
  cb.getOffset(new com.airbnb.lottie.value.LottieFrameInfo<Integer>());
  ok = true;
} catch (IllegalArgumentException e) {
  ok = false;
}
if (!ok) {
  cb = new LottieRelativeIntegerValueCallback(0); // or override getOffset
}

Type guard

// Java has no per-instance type guard; prefer a factory that cannot produce an uninitialized callback:
static LottieRelativeIntegerValueCallback offset(int staticOffset) {
  return new LottieRelativeIntegerValueCallback(staticOffset);
}
static LottieRelativeIntegerValueCallback offset(java.util.function.IntFunction<LottieFrameInfo<Integer>> fn) {
  return new LottieRelativeIntegerValueCallback() {
    @Override public Integer getOffset(LottieFrameInfo<Integer> frameInfo) { return fn.applyAsInt(/* wrap */ 0); }
  };
}

Try / catch

// Not recommended for a programmer-error exception; fix at the source instead.
// If you must isolate a third-party path:
try {
  animationView.addValueCallback(keyPath, LottieProperty.TRANSFORM_OPACITY, cb);
  animationView.invalidate(); // forces evaluation now, surfacing the throw here
} catch (IllegalArgumentException e) {
  if (e.getMessage().contains("static value")) {
    cb.setValue(0); // recover to a zero offset
  } else { throw e; }
}

Prevention

When it happens

Trigger: Constructing `new LottieRelativeIntegerValueCallback()` (no-arg) and attaching it via LottieAnimationView.addValueCallback(...LottieProperty.TIME_REMAP/etc.) or KeyPath.resolve(...) without ever calling setValue or subclassing getOffset. The throw fires from BaseKeyframeAnimation on the first frame it needs the property, i.e. during LottieDrawable.draw / onAnimationFrameEvaluate, not at construction.

Common situations: Developer copies a LottieValueCallback sample (which allows null value) but swaps in the Relative variant without adjusting initialization; assumes getOffset defaults to zero; passes the offset to the wrong overload; upgrades Lottie and a previously-tolerated null path now throws; attaches the callback to a KeyPath that animates so draw() evaluates it immediately.

Related errors


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