aeron-io/aeron · error · ClusterException
serviceId= invalid for serviceCount=
Error message
serviceId=<serviceId> invalid for serviceCount=<serviceCount>
What it means
When loading service message tracker state from a snapshot, the serviceId index must fall within the configured serviceCount (pendingServiceMessageTrackers array length). A serviceId outside [0, serviceCount) indicates the snapshot was produced by a cluster with more services than this node is configured for.
Solutions
- Set the cluster's serviceCount to match the snapshot (the service count that produced it).
- Take a fresh snapshot with the reduced service count before removing services.
- Regenerate/rebuild cluster state if services were removed and no compatible snapshot exists.
- Validate snapshot contents when migrating state between clusters with different service topologies.
Example fix
// before ctx.serviceCount(2); // snapshot has 3 services // after ctx.serviceCount(3); // matches snapshot, then snapshot again after removing a service
Defensive patterns
Strategy: validation
Validate before calling
// before load
if (configuredServiceCount < snapshotServiceCount) {
throw new IllegalStateException("serviceCount " + configuredServiceCount + " smaller than snapshot's " + snapshotServiceCount);
} Try / catch
try { cluster.start(); } catch (ClusterException e) { if (e.getMessage().contains("invalid for serviceCount")) { raiseServiceCountOrRestoreSnapshot(); } else { throw e; } } Prevention
- Snapshot before changing service count, in both directions.
- Keep aeron.cluster.service.count stable across snapshot load.
- Validate snapshot metadata when migrating between clusters.
When it happens
Trigger: onServiceMessageTracker loading state where serviceId < 0 or >= pendingServiceMessageTrackers.length, typically because the snapshot contains entries for services that no longer exist under the current serviceCount configuration.
Common situations: Reducing the number of clustered services without regenerating the snapshot; a snapshot produced with a different aeron.cluster.service.count; corrupted or hand-edited snapshot data.
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
- incompatible time unit
- greater than max size of
- less than min size of
- Aeron client instance must set…
- Aeron client must use a RethrowingErrorHandler
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/6ae36a768a4d8dd5.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-cluster/src/main/java/io/aeron/cluster/ConsensusModuleAgent.java:729
if (pendingServiceMessageTrackers.length > 0)
{
pendingServiceMessageTrackers[0].loadState(
nextServiceSessionId, logServiceSessionId, pendingMessageCapacity);
}
}
public void onLoadPendingMessageTracker(
final long nextServiceSessionId,
final long logServiceSessionId,
final int pendingMessageCapacity,
final int serviceId,
final DirectBuffer buffer,
final int offset,
final int length)
{
if (serviceId < 0 || serviceId >= pendingServiceMessageTrackers.length)
{
throw new ClusterException(
"serviceId=" + serviceId + " invalid for serviceCount=" + pendingServiceMessageTrackers.length);
}
pendingServiceMessageTrackers[serviceId].loadState(
nextServiceSessionId, logServiceSessionId, pendingMessageCapacity);
}
public void onLoadPendingMessage(
final long clusterSessionId, final DirectBuffer buffer, final int offset, final int length)
{
final int serviceId = PendingServiceMessageTracker.serviceIdFromLogMessage(clusterSessionId);
pendingServiceMessageTrackers[serviceId].appendMessage(buffer, offset, length);
}
public void onLoadTimer(
final long correlationId, final long deadline, final DirectBuffer buffer, final int offset, final int length)
{
onScheduleTimer(correlationId, deadline);View on GitHub (pinned to 6d60124e15)