{"record":{"id":"bc62169ba41bc529","repo":"airbnb/lottie-android","slug":"you-must-provide-a-static-value-in-the-constructor","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/LottieRelativeFloatValueCallback.java","lineNumber":34,"sourceCode":"\n  public LottieRelativeFloatValueCallback(@NonNull Float staticValue) {\n    super(staticValue);\n  }\n\n  @Override\n  public Float getValue(LottieFrameInfo<Float> frameInfo) {\n    float originalValue = MiscUtils.lerp(\n        frameInfo.getStartValue(),\n        frameInfo.getEndValue(),\n        frameInfo.getInterpolatedKeyframeProgress()\n    );\n    float offset = getOffset(frameInfo);\n    return originalValue + offset;\n  }\n\n  public Float getOffset(LottieFrameInfo<Float> 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":16,"sourceCodeEnd":40,"githubUrl":"https://github.com/airbnb/lottie-android/blob/05ea92e90381eb8a8ae06855ea2b74f322bebbec/lottie/src/main/java/com/airbnb/lottie/value/LottieRelativeFloatValueCallback.java#L16-L40","documentation":"Thrown by LottieRelativeFloatValueCallback.getOffset() when the internal value field is null. This callback is designed to add a relative offset to the animation's interpolated value, but it needs a base offset value to work with. The value can be set via the constructor (passing a Float), via setValue(), or by overriding getOffset()/getValue() entirely. If none of these are done, getOffset() has no offset to return and throws.","triggerScenarios":"Creating a LottieRelativeFloatValueCallback with the no-arg constructor (new LottieRelativeFloatValueCallback()) and using it as a value callback without calling setValue() or overriding getOffset(). When Lottie evaluates the animation and calls getValue() -> getOffset(), the null check at line 33 fires the throw at line 34.","commonSituations":"A developer creates the callback with the no-arg constructor intending to override getOffset() later but forgets. Subclassing LottieRelativeFloatValueCallback and not overriding getOffset() or providing a value. Copying example code that used the constructor overload but switching to no-arg. Refactoring that removed a setValue() call.","solutions":["Pass a static offset value to the constructor: new LottieRelativeFloatValueCallback(0.5f)","Call setValue(offset) on the callback instance before or during animation","Override getOffset(LottieFrameInfo<Float>) in a subclass to compute the offset dynamically without needing a static value","If you want absolute value control (not relative), use LottieValueCallback<Float> instead of LottieRelativeFloatValueCallback"],"exampleFix":"// before:\n// LottieRelativeFloatValueCallback<Float> cb = new LottieRelativeFloatValueCallback<>();\n// lottieAnimationView.addValueCallback(keyPath, property, cb);  // throws on first frame\n\n// after option 1: provide a static offset in the constructor\n// LottieRelativeFloatValueCallback cb = new LottieRelativeFloatValueCallback(0.5f);\n\n// after option 2: call setValue\n// cb.setValue(0.5f);\n\n// after option 3: override getOffset for dynamic offset\nLottieRelativeFloatValueCallback cb = new LottieRelativeFloatValueCallback() {\n  @Override public Float getOffset(LottieFrameInfo<Float> frameInfo) {\n    return frameInfo.getInterpolatedKeyframeProgress() * 100f; // dynamic\n  }\n};","handlingStrategy":"validation","validationCode":"// Ensure value is set before using the callback\nLottieRelativeFloatValueCallback createSafeCallback(float offset) {\n  return new LottieRelativeFloatValueCallback(offset); // non-null static value\n}\n\n// Or validate before use:\nvoid ensureValueSet(LottieRelativeFloatValueCallback cb) {\n  if (cb.getValue(new LottieFrameInfo<>()) == null && !hasOverride(cb)) {\n    cb.setValue(0f); // provide a default offset\n  }\n}","typeGuard":"// Check if the callback has a usable value before applying\nboolean isCallbackReady(LottieRelativeFloatValueCallback cb) {\n  try {\n    cb.getOffset(new LottieFrameInfo<Float>());\n    return true;\n  } catch (IllegalArgumentException e) {\n    return false;\n  }\n}","tryCatchPattern":"try {\n  lottieAnimationView.addValueCallback(keyPath, property, callback);\n} catch (IllegalArgumentException e) {\n  if (e.getMessage().contains(\"static value\")) {\n    callback.setValue(0f); // provide default offset\n    lottieAnimationView.addValueCallback(keyPath, property, callback);\n  } else throw e;\n}","preventionTips":["Always pass a Float value to the LottieRelativeFloatValueCallback constructor","Call setValue() if you used the no-arg constructor","Override getOffset() for dynamic offsets instead of relying on a static value"],"tags":["lottie","value-callback","animation","configuration"],"backgroundTag":null,"analyzedSha":"05ea92e90381eb8a8ae06855ea2b74f322bebbec","analyzedAt":"2026-08-14T00:51:35.636Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}