aeron-io/aeron · error · ControlProtocolException

UNKNOWN_COUNTER

UNKNOWN_COUNTER

Error message

unknown counter: ${registrationId}

What it means

Thrown by DriverConductor when a removeCounter command references a counter registrationId that has no matching CounterLink in the driver. The driver looks up the counter among its active links; if none matches it throws ControlProtocolException with code UNKNOWN_COUNTER, so the client's remove operation fails and no onUnavailableCounter/operationSucceeded is issued for it.

Solutions

  1. Pass counter.registrationId() (the long registration id), not the counterId int, to removeCounter
  2. Release each counter once; guard double-release paths by nulling the reference after release
  3. Catch ControlProtocolException with UNKNOWN_COUNTER and treat as already-released during cleanup
  4. Verify the counter still exists via CountersReader before removal (check its state is not RECORD_RECLAIMED)

Example fix

// before
long id = counter.counterId(); // wrong: int counterId, not registrationId
aeron.removeCounter(id);
// after
try {
    aeron.removeCounter(counter.registrationId());
} catch (ControlProtocolException e) {
    if (e.errorCode() == ControlProtocolException.Code.UNKNOWN_COUNTER) {
        // counter already released
    } else { throw e; }
}
Defensive patterns

Strategy: try-catch

Validate before calling

final CountersReader counters = aeron.countersReader();
if (counters.getCounterState(counter.counterId()) != CountersReader.RECORD_ALLOCATED) { return; }

Type guard

boolean isCounterLive(CountersReader r, Counter c) { return !c.isClosed() && r.getCounterState(c.counterId()) == CountersReader.RECORD_ALLOCATED; }

Try / catch

try {
    aeron.removeCounter(counter.registrationId());
} catch (ControlProtocolException e) {
    if (e.errorCode() != ControlProtocolException.Code.UNKNOWN_COUNTER) throw e;
    // already released
}

Prevention

When it happens

Trigger: Calling CountersReader/ClientConductor releaseCounter or Aeron.removeCounter() with an id that was never allocated, already released, or whose counter was freed when its owning client was timed out by the driver.

Common situations: Double-releasing counters in shutdown code; using a counter id (int) instead of the registrationId (long); releasing after driver restart; counters closed automatically on client timeout then released again by app code.

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/32492bdf7937cc64. Report an issue: GitHub.

Appendix: source

Thrown at aeron-driver/src/main/java/io/aeron/driver/DriverConductor.java:961

    void onRemoveCounter(final long registrationId, final long correlationId)
    {
        CounterLink counterLink = null;
        final ArrayList<CounterLink> counterLinks = this.counterLinks;
        for (int i = 0, size = counterLinks.size(); i < size; i++)
        {
            final CounterLink link = counterLinks.get(i);
            if (registrationId == link.registrationId())
            {
                counterLink = link;
                fastUnorderedRemove(counterLinks, i);
                break;
            }
        }

        if (null == counterLink)
        {
            throw new ControlProtocolException(UNKNOWN_COUNTER, "unknown counter: " + registrationId);
        }

        clientProxy.operationSucceeded(correlationId);
        clientProxy.onUnavailableCounter(registrationId, counterLink.counterId());
        counterLink.close();
    }

    void onClientClose(final long clientId)
    {
        final AeronClient client = findClient(clients, clientId);
        if (null != client)
        {
            client.onClosedByCommand();
        }
    }

    void onAddRcvDestination(final long registrationId, final String destinationChannel, final long correlationId)
    {

View on GitHub (pinned to 6d60124e15)