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

  1. Ensure you pass the registration id returned by addCounter (or Counter.registrationId()), not another resource's id.
  2. Separate your bookkeeping for publication/subscription/counter ids (distinct fields or maps) instead of one pooled collection.
  3. Check the id at the call site: log or assert which resource it came from before releasing.
  4. 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

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


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)