aeron-io/aeron · error · ClusterException

invalid serviceId for count of

Error message

invalid serviceId ${serviceId} for count of ${serviceCount}

What it means

RecoveryState.getSnapshotRecordingId validates the requested serviceId against the serviceCount stored in the recovery-state counter's key. If serviceId is negative or >= serviceCount, this ClusterException is thrown — the caller asked for a snapshot recording ID for a service that was not recorded.

Solutions

  1. Ensure each service container's clusteredServiceId is within [0, serviceCount).
  2. Re-take a cluster snapshot after changing the number of services or their IDs.
  3. Validate serviceId against the configured service count before calling getSnapshotRecordingId.
  4. Check cluster configuration consistency across all nodes (service ids/counts must match the snapshot).

Example fix

// before
long id = recoveryState.getSnapshotRecordingId(counterId, serviceId);
// after
if (serviceId >= 0 && serviceId < configuredServiceCount) {
    long id = recoveryState.getSnapshotRecordingId(counterId, serviceId);
}
Defensive patterns

Strategy: validation

Validate before calling

if (serviceId < 0 || serviceId >= configuredServiceCount) {
    throw new IllegalArgumentException("serviceId out of range: " + serviceId);
}

Try / catch

try {
    long id = recoveryState.getSnapshotRecordingId(counterId, serviceId);
} catch (ClusterException e) {
    if (e.getMessage().contains("invalid serviceId")) {
        // fix clusteredServiceId config or re-snapshot cluster
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling getSnapshotRecordingId(counterId, serviceId) where serviceId < 0 or serviceId >= the service count encoded in the recovery state counter key.

Common situations: Service registered with an id higher than the configured service count (clusteredServiceId misconfiguration); snapshot taken with fewer services than are now querying recovery state; changed service configuration between snapshot and recovery.

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/8df75c3ffafa78cc. Report an issue: GitHub.

Appendix: source

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

     *
     * @param counters  to search within.
     * @param counterId for the active recovery counter.
     * @param serviceId for the snapshot required.
     * @return the count of replay terms if found otherwise {@link Aeron#NULL_VALUE}.
     */
    public static long getSnapshotRecordingId(final CountersReader counters, final int counterId, final int serviceId)
    {
        final DirectBuffer buffer = counters.metaDataBuffer();

        if (counters.getCounterState(counterId) == RECORD_ALLOCATED &&
            counters.getCounterTypeId(counterId) == RECOVERY_STATE_TYPE_ID)
        {
            final int recordOffset = CountersReader.metaDataOffset(counterId);

            final int serviceCount = buffer.getInt(recordOffset + KEY_OFFSET + SERVICE_COUNT_OFFSET);
            if (serviceId < 0 || serviceId >= serviceCount)
            {
                throw new ClusterException("invalid serviceId " + serviceId + " for count of " + serviceCount);
            }

            return buffer.getLong(
                recordOffset + KEY_OFFSET + SNAPSHOT_RECORDING_IDS_OFFSET + (serviceId * SIZE_OF_LONG));
        }

        throw new ClusterException("active counter not found " + counterId);
    }
}

View on GitHub (pinned to 6d60124e15)