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
- Pass a static offset in the constructor: `new LottieRelativeIntegerValueCallback(5)` (or whichever int you need).
- If the offset changes at runtime, keep the no-arg constructor but call `callback.setValue(offset)` before or while the animation runs.
- 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).
- 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
- Never call the no-arg LottieRelative*ValueCallback constructor without immediately setValue-ing or overriding getOffset.
- Treat 'must provide a static value' as a build-time defect; fix it, do not catch it.
- Add a unit test that constructs your callback and calls getOffset(new LottieFrameInfo<>()) to fail fast before the animation does.
- Keep a single factory per callback so the offset is always supplied; ban raw `new LottieRelative...` in review.
- When upgrading Lottie, re-run any screen that uses dynamic value callbacks on the first frame.
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
- You must provide a static value in the constructor , call se
- LottieAnimator does not support getStartDelay.
- LottieAnimator does not support setStartDelay.
- LottieAnimator does not support setDuration.
- LottieAnimator does not support setInterpolator.
AI-assisted analysis of airbnb/lottie-android@05ea92e903 (2026-08-14).
Data as JSON: /api/errors/4f70bddcc81116bc.
Report an issue: GitHub.