daimajia/AndroidViewAnimations · error · RuntimeException

Can not be less than -1, -1 is infinite loop

Error message

Can not be less than -1, -1 is infinite loop

What it means

AnimationComposer.repeat(int times) validates the loop-count argument at composition time: the minimum legal value is INFINITE (-1), which means loop forever. Any value below -1 (e.g. -2) is rejected with a RuntimeException because there is no meaningful repeat count less than infinite; 0 means play once (repeat disabled), positive values repeat that many times.

Solutions

  1. Clamp before calling: pass Math.max(-1, times) or map any negative to INFINITE (-1)
  2. If you want a single playthrough, pass 0 or omit repeat(); only -1 means infinite
  3. Validate the count at its source (user input/API payload) and normalize there
  4. If the count is untrusted, wrap the composer chain in try-catch and fall back to a sane default like repeat(1)

Example fix

// before
int times = serverConfig.getRepeatCount(); // may be -3
YoYo.with(Techniques.Bounce).repeat(times).playOn(view);
// after
int times = serverConfig.getRepeatCount();
if (times < -1) times = -1; // INFINITE
YoYo.with(Techniques.Bounce).repeat(times).playOn(view);
Defensive patterns

Strategy: validation

Validate before calling

static int safeRepeat(int times) {
    final int INFINITE = -1;
    if (times < INFINITE) {
        throw new IllegalArgumentException("repeat must be >= -1 (INFINITE), got " + times);
    }
    return times;
}
// usage: YoYo.with(Techniques.Tada).repeat(safeRepeat(userCount)).playOn(view);

Try / catch

try {
    YoYo.with(Techniques.Bounce).repeat(times).playOn(view);
} catch (RuntimeException e) {
    if (e.getMessage() != null && e.getMessage().contains("less than -1")) {
        Log.w("YoYo", "bad repeat count " + times + ", using default");
        YoYo.with(Techniques.Bounce).repeat(1).playOn(view);
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling YoYo.with(...).repeat(n) with n < -1 — typically a computed count that drifted negative (e.g. repeat(count - 2) when count <= 1), a misdecoded remote-config value, or confusing -1 semantics with other negative sentinels.

Common situations: Passing user-supplied or server-supplied repeat counts without clamping; off-by-one arithmetic on repeat counters; developers using negative values as 'disabled' flags instead of 0.

Related errors


AI-assisted analysis of daimajia/AndroidViewAnimations@6a35c466d2 (2026-09-08). Data as JSON: /api/errors/86557f16fd5cde38. Report an issue: GitHub.

Appendix: source

Thrown at library/src/main/java/com/daimajia/androidanimations/library/YoYo.java:156

        public AnimationComposer pivot(float pivotX, float pivotY) {
            this.pivotX = pivotX;
            this.pivotY = pivotY;
            return this;
        }

        public AnimationComposer pivotX(float pivotX) {
            this.pivotX = pivotX;
            return this;
        }

        public AnimationComposer pivotY(float pivotY) {
            this.pivotY = pivotY;
            return this;
        }

        public AnimationComposer repeat(int times) {
            if (times < INFINITE) {
                throw new RuntimeException("Can not be less than -1, -1 is infinite loop");
            }
            repeat = times != 0;
            repeatTimes = times;
            return this;
        }

        public AnimationComposer repeatMode(int mode) {
            repeatMode = mode;
            return this;
        }

        public AnimationComposer withListener(Animator.AnimatorListener listener) {
            callbacks.add(listener);
            return this;
        }

        public AnimationComposer onStart(final AnimatorCallback callback) {
            callbacks.add(new EmptyAnimatorListener() {

View on GitHub (pinned to 6a35c466d2)