aeron-io/aeron · error · AeronException

reentrant calls not permitted during callbacks

Error message

reentrant calls not permitted during callbacks

What it means

ensureNotReentrant() throws this when a client callback (e.g. AvailableImageHandler, UnavailableCounterHandler, ReservedValueSupplier) makes a re-entrant call back into the ClientConductor that would mutate state while the conductor is iterating inside its own callback. Re-entrant mutation during callbacks can corrupt conductor state, so it is forbidden.

Solutions

  1. Do not call state-changing client APIs from inside callbacks; instead enqueue the action and execute it on another thread
  2. Use Aeron's async APIs: schedule close/add work outside the callback via an executor
  3. Use AtomicCounter or queue signals from the callback and act in your own loop
  4. If teardown is needed, defer with a separate executor thread that performs the close

Example fix

// before
subscription.onUnavailableImage(img -> aeron.close()); // reentrant call
// after
subscription.onUnavailableImage(img -> closeExecutor.execute(() -> aeron.close()));
Defensive patterns

Strategy: try-catch

Try / catch

try { conductorCall(); } catch (AeronException e) { if (e.getMessage().contains("reentrant")) { deferToOtherThread(action); } else { throw e; } }

Prevention

When it happens

Trigger: From within an onAvailableImage/onUnavailableImage/onAvailableCounter callback, calling APIs that require conductor state changes such as Aeron.close(), addSubscription/addPublication removal, or other state-mutating calls on the same client thread.

Common situations: Calling aeron.close() inside an unavailable-image handler; adding/removing subscriptions inside image callbacks; complex teardown logic embedded in listeners running on the conductor thread.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at aeron-client/src/main/java/io/aeron/ClientConductor.java:1728

    private void ensureActive()
    {
        if (isClosed)
        {
            throw new AeronException("Aeron client is closed");
        }

        if (isTerminating)
        {
            throw new AeronException("Aeron client is terminating");
        }
    }

    private void ensureNotReentrant()
    {
        if (isInCallback)
        {
            throw new AeronException("reentrant calls not permitted during callbacks");
        }
    }

    private LogBuffers logBuffers(final long registrationId, final String logFileName, final String channel)
    {
        LogBuffers logBuffers = logBuffersByIdMap.get(registrationId);
        if (null == logBuffers)
        {
            try
            {
                logBuffers = logBuffersFactory.map(logFileName);

                if (ctx.preTouchMappedMemory())
                {
                    logBuffers.preTouch();
                }

                logBuffersByIdMap.put(registrationId, logBuffers);

View on GitHub (pinned to 6d60124e15)