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
- Reduce the number of clustered services in the container configuration.
- Do not pass more recording IDs than the service count / shrink the snapshotRecordingIds array.
- Increase the counters key capacity only if you control the Aeron counters manager buffer sizes.
- 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
- Cap configured service count below the key-capacity limit (64 / 8 minus header entries).
- Pass exactly the configured service count of recording IDs to allocate.
- Assert array length in configuration loading, not at allocation time.
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
- active counter not found
- invalid state counter code
- Invalid role counter code
- failed to start service=<ctx.serviceId()> leadershipTermId=
- invalid serviceId for count of
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)