aeron-io/aeron · error · ClusterException

exceeds max key length

Error message

${keyLength} exceeds max key length ${MAX_KEY_LENGTH}

What it means

RecoveryState.allocate encodes the snapshotRecordingIds array into a counter's key. The total key length (fixed header plus one long per service) must fit within MAX_KEY_LENGTH (the counter key capacity). If serviceCount is too large, the computed keyLength overflows the key and this ClusterException is thrown before writing.

Solutions

  1. Reduce the number of clustered services in the container configuration.
  2. Do not pass more recording IDs than the service count / shrink the snapshotRecordingIds array.
  3. Increase the counters key capacity only if you control the Aeron counters manager buffer sizes.
  4. Check for a bug passing the wrong array (e.g. unbounded list) to allocate.

Example fix

// before
RecoveryState.allocate(countersManager, tempBuffer, Integer.MAX_VALUE, snapshotIds, ...);
// after
if (snapshotIds.length <= MAX_SERVICES) {
    RecoveryState.allocate(countersManager, tempBuffer, recoverable, snapshotIds, ...);
}
Defensive patterns

Strategy: validation

Validate before calling

int keyLength = RecoveryState.SNAPSHOT_RECORDING_IDS_OFFSET + serviceCount * Long.BYTES;
if (keyLength > RecoveryState.MAX_KEY_LENGTH) {
    throw new IllegalArgumentException("too many services: " + serviceCount);
}

Try / catch

try {
    RecoveryState.allocate(countersManager, tempBuffer, snapshotIds, ...);
} catch (ClusterException e) {
    if (e.getMessage().contains("exceeds max key length")) {
        // reduce service count or fail fast with clearer message
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling RecoveryState.allocate with a snapshotRecordingIds array whose length (service count) makes SNAPSHOT_RECORDING_IDS_OFFSET + serviceCount * SIZE_OF_LONG exceed MAX_KEY_LENGTH.

Common situations: Running a cluster configured with an extremely large services count (aeron.cluster.services.count or many registered services) exceeding Aeron counter key capacity; misconfigured counter key buffer size.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12). Data as JSON: /api/errors/777544d02067c8b0. Report an issue: GitHub.

Appendix: source

Thrown at aeron-cluster/src/main/java/io/aeron/cluster/service/RecoveryState.java:132

        final long logPosition,
        final long timestamp,
        final int clusterId,
        final long... snapshotRecordingIds)
    {
        final ExpandableArrayBuffer buffer = new ExpandableArrayBuffer(256);

        buffer.putLong(LEADERSHIP_TERM_ID_OFFSET, leadershipTermId);
        buffer.putLong(LOG_POSITION_OFFSET, logPosition);
        buffer.putLong(TIMESTAMP_OFFSET, timestamp);
        buffer.putInt(CLUSTER_ID_OFFSET, clusterId);

        final int serviceCount = snapshotRecordingIds.length;
        buffer.putInt(SERVICE_COUNT_OFFSET, serviceCount);

        final int keyLength = SNAPSHOT_RECORDING_IDS_OFFSET + (serviceCount * SIZE_OF_LONG);
        if (keyLength > MAX_KEY_LENGTH)
        {
            throw new ClusterException(keyLength + " exceeds max key length " + MAX_KEY_LENGTH);
        }

        for (int i = 0; i < serviceCount; i++)
        {
            buffer.putLong(SNAPSHOT_RECORDING_IDS_OFFSET + (i * SIZE_OF_LONG), snapshotRecordingIds[i]);
        }

        final int labelOffset = BitUtil.align(keyLength, SIZE_OF_INT);
        int labelLength = 0;
        labelLength += buffer.putStringWithoutLengthAscii(labelOffset + labelLength, NAME);
        labelLength += buffer.putLongAscii(keyLength + labelLength, leadershipTermId);
        labelLength += buffer.putStringWithoutLengthAscii(labelOffset + labelLength, " logPosition=");
        labelLength += buffer.putLongAscii(labelOffset + labelLength, logPosition);
        labelLength += buffer.putStringWithoutLengthAscii(labelOffset + labelLength, " clusterId=");
        labelLength += buffer.putIntAscii(labelOffset + labelLength, clusterId);

        return aeron.addCounter(RECOVERY_STATE_TYPE_ID, buffer, 0, keyLength, buffer, labelOffset, labelLength);
    }

View on GitHub (pinned to 6d60124e15)