aeron-io/aeron · error · AeronException

reentrant calls not permitted during callbacks

Error message

reentrant calls not permitted during callbacks

What it means

AeronArchive serializes access to its control protocol state machine. While the client is inside a callback (e.g. an AeronFragmentHandler or ControlResponsePoller handler it invoked), reentering AeronArchive API methods would corrupt the state machine, so ensureNotReentrant() throws this AeronException.

Solutions

  1. Do not call AeronArchive methods from within its callbacks; record the event in a queue and process it after the callback returns.
  2. Use a separate thread (e.g. an Agent/executor) to issue follow-up archive requests.
  3. Restructure the callback to only mutate local state; run the next archive command in your own driver loop after the poll returns.
  4. If truly needing reentrancy, use distinct AeronArchive instances for the callback path and the request path.

Example fix

// before
archive.listRecording(archive.listRecordings(0, 10)[0].recordingId()); // inside an archive callback
// after
pendingRequests.offer(recordingId); // handled after callback returns on same thread
long id = pendingRequests.poll();
if (id > 0) { archive.listRecording(id); }
Defensive patterns

Strategy: try-catch

Try / catch

try { archive.pollForNextResponse(correlationId); } catch (AeronException e) { if (e.getMessage().contains("reentrant")) { log.warn("archive call from inside callback; defer it"); deferred.offer(() -> archive.pollForNextResponse(correlationId)); } }

Prevention

When it happens

Trigger: From inside a callback delivered by AeronArchive (e.g. the controlResponsePoller's fragment handler or a recording signal handler), calling back into AeronArchive methods such as pollForNextResponse, nextCorrelationId, or send/receive operations.

Common situations: Dispatching work from an archive callback and immediately issuing new archive requests in the same callback; nested await*() calls inside a handler; legacy code updated to a version that added the reentrancy guard.

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

Appendix: source

Thrown at aeron-archive/src/main/java/io/aeron/archive/client/AeronArchive.java:2607

        final State currentState = state;
        if (State.CONNECTED != currentState)
        {
            if (State.CLOSED == currentState)
            {
                throw new ArchiveException("client is closed");
            }
            else
            {
                close();
            }
        }
    }

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

    private void state(final State newState)
    {
        if (State.CLOSED != state)
        {
            state = newState;
        }
    }

    /**
     * Common configuration properties for communicating with an Aeron archive.
     */
    @Config(existsInC = false)
    public static final class Configuration
    {
        private Configuration()

View on GitHub (pinned to 6d60124e15)