aeron-io/aeron · error · AeronException
registration id is not a Counter
Error message
registration id is not a Counter: <simpleName>
What it means
ClientConductor.releaseCounter (or equivalent lookup by registration id) fetches the resource from resourceByRegIdMap and, if a resource exists that is not a Counter, throws AeronException naming its simple class name. The registration id you supplied identifies a different client resource type (e.g. a Publication, Subscription, or Image), and releasing it through the Counter API would corrupt the conductor's bookkeeping.
Solutions
- Ensure you pass the registration id returned by addCounter (or Counter.registrationId()), not another resource's id.
- Separate your bookkeeping for publication/subscription/counter ids (distinct fields or maps) instead of one pooled collection.
- Check the id at the call site: log or assert which resource it came from before releasing.
- Prefer calling counter.close() on the Counter object, which routes through the correct release path.
Example fix
// before
long id = idsFromAsyncCommands.get("resource"); // could be a publication id
aeronClient.releaseCounter(id);
// throws AeronException: registration id is not a Counter
// after
Counter counter = countersById.get(registrationId);
if (counter != null) {
counter.close(); // releases via the correct path
} Defensive patterns
Strategy: type-guard
Validate before calling
if (!(aeronClient.getResource(registrationId) instanceof Counter)) {
throw new IllegalStateException("id " + registrationId + " is not a Counter");
} Type guard
boolean isCounterRegId(long registrationId) {
Object r = resourceByRegIdMap.get(registrationId); // or your own registry
return r instanceof Counter;
} Try / catch
try {
aeronClient.releaseCounter(registrationId);
} catch (AeronException e) {
if (e.getMessage().startsWith("registration id is not a Counter")) {
logger.error("wrong resource id {} passed to releaseCounter", registrationId, e);
} else {
throw e;
}
} Prevention
- Store Counter registration ids separately from publication/subscription ids
- Call counter.close() on the Counter object rather than releasing by raw id
- Log the resource type whenever you record a registration id
- Keep one source of truth for ids returned by each add* call
When it happens
Trigger: Calling AeronClient/aeron.releaseCounter(counterRegistrationId) or ClientConductor's Counter release path with a registration id that actually belongs to a Publication, ExclusivePublication, Subscription, or Image registered on the same client.
Common situations: Mixing up correlation ids collected from async addPublication/addCounter commands in a shared registry; passing an image or subscription id saved from a callback; refactors that unify resource ids into one map/array and index into the wrong slot; copy-paste between release calls.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- Counter id is not allocated:
- invalid state counter code
- recording has stopped unexpectedly
- Invalid role counter code
- exceeds max key length
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/fefb15acf33f7cf1.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-client/src/main/java/io/aeron/ClientConductor.java:1376
}
}
void asyncRemoveCounter(final long counterRegistrationId)
{
clientLock.lock();
try
{
if (NULL_VALUE == counterRegistrationId || isTerminating || isClosed)
{
return;
}
ensureNotReentrant();
final Object resource = resourceByRegIdMap.get(counterRegistrationId);
if (null != resource && !(resource instanceof Counter))
{
throw new AeronException("registration id is not a Counter: " +
resource.getClass().getSimpleName());
}
final Counter counter = (Counter)resource;
if (null != counter)
{
resourceByRegIdMap.remove(counterRegistrationId);
counter.internalClose();
}
if (asyncCommandIdSet.remove(counterRegistrationId) || null != counter)
{
asyncCommandIdSet.add(driverProxy.removeCounter(counterRegistrationId));
}
}
finally
{
clientLock.unlock();View on GitHub (pinned to 6d60124e15)