aeron-io/aeron · error · IllegalStateException
failed to offer async replay response
Error message
failed to offer async replay response
What it means
ControlSession.asyncSendOkResponse throws IllegalStateException when the internal offer of an async replay's OK response (via the control response proxy) fails, i.e. the response could not be enqueued for delivery to the client. This indicates the session's response publication could not accept the message at the moment of the offer.
Solutions
- Ensure the client keeps its control-response subscription open until it receives the OK reply for the replay request
- On failure, the archive conductor should abort/close the session so the client reconnects and retries the replay request
- Check client-side consumption: a stalled poller thread on the response subscription causes persistent back-pressure
- Verify control response channel connectivity (endpoints, networks, driver running) on both sides
Example fix
// client-side retry pattern
long correlationId = archive.startReplay(...);
// poll with timeout; on failure reconnect and resubmit
if (!awaitOk(archive, correlationId, timeoutMs))
{
archive.close();
archive = ArchiveProxy.connect(controlResponseChannel);
correlationId = archive.startReplay(...);
} Defensive patterns
Strategy: retry
Validate before calling
if (controlSession == null || controlSession.isClosed()) { throw new IllegalStateException("control session not usable for async replay response"); } Try / catch
try { session.asyncSendOkResponse(correlationId, replaySessionId); }
catch (IllegalStateException e)
{
if (e.getMessage().contains("failed to offer async replay response")) { /* abort session / client retries request */ }
else { throw e; }
} Prevention
- Keep the client response subscription polled so back-pressure never blocks offers
- Retry briefly on back-pressure before treating the offer as failed (archive-side)
- Ensure replay requests are only issued on a healthy, connected control session
- Verify response channel configuration and network path before starting replays
When it happens
Trigger: Calling asyncSendOkResponse (e.g. to acknowledge a start-replay request) when the response publication offer fails — publication back-pressured/blocked, not connected, closed, or at max position, so the OK reply cannot be offered.
Common situations: Client disconnected just as the replay start was accepted; response publication back-pressured by a slow consumer; session being closed concurrently by the conductor; response channel misconfigured so no subscriber is attached.
Understand the failure class
Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.
Related errors
- ControlSession.RESPONSE_NOT_CONNECTED_MSG + ": " + session
- response publication is closed: " + session
- response publication is at max position: " + session
- name + " cannot be negative: value=" + value
- failed to send replay token request
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/0a82cd7fc5fc5f79.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-archive/src/main/java/io/aeron/archive/ControlSession.java:743
queueResponse(correlationId, relevantId, code, errorMessage);
}
else
{
activityDeadlineMs = NULL_VALUE;
}
}
void asyncSendOkResponse(final long correlationId, final long replaySessionId)
{
if (!asyncResponseQueue.offer(() -> controlResponseProxy.sendResponse(
controlSessionId,
correlationId,
replaySessionId,
OK,
null,
this)))
{
throw new IllegalStateException("failed to offer async replay response");
}
}
boolean sendDescriptor(final long correlationId, final UnsafeBuffer descriptorBuffer)
{
assertCalledOnConductorThread();
final boolean sent =
controlResponseProxy.sendDescriptor(controlSessionId, correlationId, descriptorBuffer, this);
if (!sent)
{
updateActivityDeadline(cachedEpochClock.time());
}
else
{
activityDeadlineMs = NULL_VALUE;
}
return sent;
}View on GitHub (pinned to 6d60124e15)