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
- Do not call AeronArchive methods from within its callbacks; record the event in a queue and process it after the callback returns.
- Use a separate thread (e.g. an Agent/executor) to issue follow-up archive requests.
- Restructure the callback to only mutate local state; run the next archive command in your own driver loop after the poll returns.
- 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
- Never call AeronArchive methods from within its own callbacks; enqueue work for after the callback.
- Use a queue + same-thread drain loop after poll returns to issue follow-up commands.
- Keep callback bodies short and side-effect-free with respect to the archive client.
- Document the no-reentrancy contract wherever archive callbacks are registered.
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
- Aeron client instance must set…
- Aeron client must use a RethrowingErrorHandler
- segment file length not a power of 2
- segment file length not in valid range
- Unable to derive…
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)