{"record":{"id":"4f70bddcc81116bc","repo":"airbnb/lottie-android","slug":"you-must-provide-a-static-value-in-the-constructor-4f70bd","errorCode":null,"errorMessage":"You must provide a static value in the constructor , call setValue, or override getValue.","messagePattern":"You must provide a static value in the constructor , call setValue, or override getValue\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"lottie/src/main/java/com/airbnb/lottie/value/LottieRelativeIntegerValueCallback.java","lineNumber":27,"sourceCode":"@SuppressWarnings({\"WeakerAccess\", \"unused\"})\npublic class LottieRelativeIntegerValueCallback extends LottieValueCallback<Integer> {\n  @Override\n  public Integer getValue(LottieFrameInfo<Integer> frameInfo) {\n    int originalValue = MiscUtils.lerp(\n        frameInfo.getStartValue(),\n        frameInfo.getEndValue(),\n        frameInfo.getInterpolatedKeyframeProgress()\n    );\n    int newValue = getOffset(frameInfo);\n    return originalValue + newValue;\n  }\n\n  /**\n   * Override this to provide your own offset on every frame.\n   */\n  public Integer getOffset(LottieFrameInfo<Integer> frameInfo) {\n    if (value == null) {\n      throw new IllegalArgumentException(\"You must provide a static value in the constructor \" +\n          \", call setValue, or override getValue.\");\n    }\n    return value;\n  }\n}\n","sourceCodeStart":9,"sourceCodeEnd":33,"githubUrl":"https://github.com/airbnb/lottie-android/blob/05ea92e90381eb8a8ae06855ea2b74f322bebbec/lottie/src/main/java/com/airbnb/lottie/value/LottieRelativeIntegerValueCallback.java#L9-L33","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nLottieRelativeIntegerValueCallback cb = new LottieRelativeIntegerValueCallback();\nanimationView.addValueCallback(keyPath, LottieProperty.TRANSFORM_OPACITY, cb); // throws on first frame\n\n// after — option A: static offset\nLottieRelativeIntegerValueCallback cb = new LottieRelativeIntegerValueCallback(20);\n\n// after — option B: per-frame offset\ncb = new LottieRelativeIntegerValueCallback() {\n  @Override\n  public Integer getOffset(LottieFrameInfo<Integer> frameInfo) {\n    return (int) (frameInfo.getOverallProgress() * 100);\n  }\n};","handlingStrategy":"validation","validationCode":"LottieRelativeIntegerValueCallback cb;\n// before attaching, ensure it can produce an offset:\nboolean ok = false;\ntry {\n  // dry-run the same path the animation will take\n  cb.getOffset(new com.airbnb.lottie.value.LottieFrameInfo<Integer>());\n  ok = true;\n} catch (IllegalArgumentException e) {\n  ok = false;\n}\nif (!ok) {\n  cb = new LottieRelativeIntegerValueCallback(0); // or override getOffset\n}","typeGuard":"// Java has no per-instance type guard; prefer a factory that cannot produce an uninitialized callback:\nstatic LottieRelativeIntegerValueCallback offset(int staticOffset) {\n  return new LottieRelativeIntegerValueCallback(staticOffset);\n}\nstatic LottieRelativeIntegerValueCallback offset(java.util.function.IntFunction<LottieFrameInfo<Integer>> fn) {\n  return new LottieRelativeIntegerValueCallback() {\n    @Override public Integer getOffset(LottieFrameInfo<Integer> frameInfo) { return fn.applyAsInt(/* wrap */ 0); }\n  };\n}","tryCatchPattern":"// Not recommended for a programmer-error exception; fix at the source instead.\n// If you must isolate a third-party path:\ntry {\n  animationView.addValueCallback(keyPath, LottieProperty.TRANSFORM_OPACITY, cb);\n  animationView.invalidate(); // forces evaluation now, surfacing the throw here\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"static value\")) {\n    cb.setValue(0); // recover to a zero offset\n  } else { throw e; }\n}","preventionTips":["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."],"tags":["lottie","android","animation","value-callback","runtime"],"backgroundTag":null,"analyzedSha":"05ea92e90381eb8a8ae06855ea2b74f322bebbec","analyzedAt":"2026-08-14T00:51:35.636Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}