TheAlgorithms/Java · error · IllegalArgumentException

Cannot compare PN-Counters with different number of nodes

Error message

Cannot compare PN-Counters with different number of nodes

What it means

PNCounter.compare() requires both counters to have the same cluster size n, because it iterates element-wise over node indices [0, n). The PN-Counter CRDT model assumes a fixed, agreed-upon cluster topology; comparing counters from different-sized clusters is undefined and would cause IndexOutOfBoundsException on the underlying maps if not guarded.

Source

Thrown at src/main/java/com/thealgorithms/datastructures/crdt/PNCounter.java:76

     * Gets the total value of the counter by subtracting the decrement counter from the increment counter.
     *
     * @return The total value of the counter.
     */
    public int value() {
        int sumP = pCounter.values().stream().mapToInt(Integer::intValue).sum();
        int sumN = nCounter.values().stream().mapToInt(Integer::intValue).sum();
        return sumP - sumN;
    }

    /**
     * Compares the state of this PN-Counter with another PN-Counter.
     *
     * @param other The other PN-Counter to compare with.
     * @return True if the state of this PN-Counter is less than or equal to the state of the other PN-Counter.
     */
    public boolean compare(PNCounter other) {
        if (this.n != other.n) {
            throw new IllegalArgumentException("Cannot compare PN-Counters with different number of nodes");
        }
        for (int i = 0; i < n; i++) {
            if (this.pCounter.get(i) > other.pCounter.get(i) && this.nCounter.get(i) > other.nCounter.get(i)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Merges the state of this PN-Counter with another PN-Counter.
     *
     * @param other The other PN-Counter to merge with.
     */
    public void merge(PNCounter other) {
        if (this.n != other.n) {
            throw new IllegalArgumentException("Cannot merge PN-Counters with different number of nodes");
        }

View on GitHub (pinned to fdfb9a395b)

Solutions

  1. Ensure all PNCounter instances in a cluster are constructed with the same n value
  2. When scaling the cluster, recreate all counters with the new n before comparing or merging
  3. Check counter.n equality (or expose it) before calling compare()

Example fix

// before
PNCounter a = new PNCounter(0, 3);
PNCounter b = new PNCounter(0, 5);
a.compare(b); // throws

// after — agree on cluster size at construction time
int clusterSize = ClusterConfig.getNodeCount();
PNCounter a = new PNCounter(0, clusterSize);
PNCounter b = new PNCounter(0, clusterSize);
a.compare(b);
Defensive patterns

Strategy: validation

Validate before calling

// Expose n or track it externally, then verify before compare.
// PNCounter.n is package-private; if accessible:
if (thisCounter.n != otherCounter.n) {
    throw new IllegalStateException("Cluster size mismatch");
}
thisCounter.compare(otherCounter);

Try / catch

try {
    return a.compare(b);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("different number of nodes")) {
        // topology mismatch — skip comparison or resync
        return false;
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling counter1.compare(counter2) where counter1 was constructed as new PNCounter(id, 3) and counter2 as new PNCounter(id, 5). Also when counters are deserialized from different deployments or cluster generations.

Common situations: Dynamically scaling a cluster (adding new nodes) without recreating all counters with the new n. Mixing counters from different environments (staging vs. production). Counters received over the network from a peer with a different cluster configuration.

Related errors


AI-assisted analysis of TheAlgorithms/Java@fdfb9a395b (2026-08-13). Data as JSON: /api/errors/339295ae1e8b7409. Report an issue: GitHub.