apache/dubbo · error · IllegalArgumentException

update value {update} < 0

Error message

update value {update} < 0

What it means

Thrown by AtomicPositiveInteger.compareAndSet when the update value is negative. CAS atomically swaps to 'update' only if the current value equals 'expect'; since 'update' must remain non-negative per the class invariant, a negative update is rejected before the CAS is attempted.

Source

Thrown at dubbo-common/src/main/java/org/apache/dubbo/common/utils/AtomicPositiveInteger.java:87

    }

    public final int getAndAdd(int delta) {
        if (delta < 0) {
            throw new IllegalArgumentException("delta " + delta + " < 0");
        }
        return INDEX_UPDATER.getAndAdd(this, delta) & Integer.MAX_VALUE;
    }

    public final int addAndGet(int delta) {
        if (delta < 0) {
            throw new IllegalArgumentException("delta " + delta + " < 0");
        }
        return INDEX_UPDATER.addAndGet(this, delta) & Integer.MAX_VALUE;
    }

    public final boolean compareAndSet(int expect, int update) {
        if (update < 0) {
            throw new IllegalArgumentException("update value " + update + " < 0");
        }
        return INDEX_UPDATER.compareAndSet(this, expect, update);
    }

    public final boolean weakCompareAndSet(int expect, int update) {
        if (update < 0) {
            throw new IllegalArgumentException("update value " + update + " < 0");
        }
        return INDEX_UPDATER.weakCompareAndSet(this, expect, update);
    }

    @Override
    public byte byteValue() {
        return (byte) get();
    }

    @Override
    public short shortValue() {

View on GitHub (pinned to 3a3043227f)

Solutions

  1. Ensure the computed 'update' is always >= 0; mask with (v & Integer.MAX_VALUE) if wraparound is acceptable.
  2. Rethink the algorithm: AtomicPositiveInteger is designed for round-robin indices that only ever grow/wrap, not arbitrary state machines.
  3. Switch to AtomicInteger if the value legitimately needs to be negative.

Example fix

// before
api.compareAndSet(old, next); // next may be negative

// after
api.compareAndSet(old, next < 0 ? 0 : next);
Defensive patterns

Strategy: validation

Validate before calling

int update = /* ... */;
if (update < 0) update = 0; // or (update & Integer.MAX_VALUE)
api.compareAndSet(expect, update);

Type guard

static boolean isNonNegative(int v) { return v >= 0; }

Prevention

When it happens

Trigger: AtomicPositiveInteger.compareAndSet(expect, update) where update < 0. The 'expect' value is not constrained (it may not match), but 'update' must be non-negative.

Common situations: Lock-free algorithm ported from AtomicInteger where the desired new state can be negative; a computed 'update' with a sign error; off-by-one in a CAS loop. Indicates the caller's algorithm is incompatible with the non-negative contract.

Related errors


AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14). Data as JSON: /api/errors/cc3f9b8604f868dd. Report an issue: GitHub.