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

  1. Ensure the client keeps its control-response subscription open until it receives the OK reply for the replay request
  2. On failure, the archive conductor should abort/close the session so the client reconnects and retries the replay request
  3. Check client-side consumption: a stalled poller thread on the response subscription causes persistent back-pressure
  4. 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

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


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)