aeron-io/aeron · error · ClusterException

active counter not found

Error message

active counter not found ${counterId}

What it means

getSnapshotRecordingId looks up the recovery-state counter by counterId in the CountersReader; if no active counter exists for that id (not found or freed), it throws this ClusterException instead of returning a recording id.

Solutions

  1. Only query recovery state during the recovery phase while the counter is alive.
  2. Refresh/rediscover the recovery-state counterId (e.g. via RecoveryState.findCounter) instead of caching it.
  3. Check that the counters file being read is the same one where recovery state was allocated (correct cluster directory).
  4. Guard with RecoveryState.hasCounter/counters reader lookup before calling.

Example fix

// before
long recId = recoveryState.getSnapshotRecordingId(counterId, serviceId);
// after
if (RecoveryState.findCounter(countersReader) != CountersReader.NULL_COUNTER_ID) {
    long recId = recoveryState.getSnapshotRecordingId(counterId, serviceId);
}
Defensive patterns

Strategy: validation

Validate before calling

int found = RecoveryState.findCounter(countersReader);
if (found == CountersReader.NULL_COUNTER_ID) {
    // no active recovery state; skip query
}

Try / catch

try {
    long id = recoveryState.getSnapshotRecordingId(counterId, serviceId);
} catch (ClusterException e) {
    if (e.getMessage().contains("active counter not found")) {
        // rediscover counterId via RecoveryState.findCounter
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling RecoveryState.getSnapshotRecordingId with a counterId that is no longer allocated in the CountersReader — e.g. the recovery state counter was freed after recovery completed, or a stale/incorrect counterId was passed.

Common situations: Querying recovery state after recovery finished and the counter was released; service using a cached counterId across a cluster restart; race where the counter was freed while the service still held the id.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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

Appendix: source

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

    {
        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)