aeron-io/aeron · error · TimeoutException

failed to send replay request

Error message

failed to send replay request

What it means

Thrown by ReplicationSession.replay() when the archive proxy repeatedly fails to enqueue the replay request to the source archive's control endpoint, and the action timeout (default aeron.archive.replication.timeout or Archive.Configuration ACTION_TIMEOUT) elapses. archiveProxy.replay() returns false when the underlying publication cannot accept the control message (not connected, no window/credit, or back-pressured), so the state machine retries until the deadline. It signals that the replication could never get its replay request to the source archive.

Solutions

  1. Verify the source archive is running and reachable: check its control-request channel/port and test connectivity (ping/nc) from the destination host.
  2. Check source archive logs for a control session rejection or termination around the failure time; fix auth/session issues and re-run the replication.
  3. Increase Aeron's replication action timeout (aeron.archive.replication.timeout, or the replicationTimeout argument to AeronArchive.replicate) so transient back-pressure can clear.
  4. Validate the replicationChannel URI (endpoint, control-mode) — ensure the endpoint resolves and wildcard ports can be resolved on both sides.
  5. Confirm both drivers are up and sender/receiver statuses are active (use Aeronstat / driver status) before retrying.

Example fix

// before: default short timeout on a slow WAN
archive.replicate(srcRecordingId, liveChannel, liveStreamId, dstRecordingId);
// after: explicit replication timeout of 30s
archive.replicate(
    srcRecordingId,
    dstRecordingId,
    liveChannel,
    liveStreamId,
    replayPosition,
    AeronArchive.NULL_LENGTH,
    replicationChannel,
    replicationChannelStreamId,
    TimeUnit.SECONDS.toNanos(30) /* replicationTimeout */);
Defensive patterns

Strategy: retry

Validate before calling

// Before starting replication, check source archive reachability
try (AeronArchive archive = AeronArchive.connect(archiveCtx))
{
    // throws if the control channel is not usable
    archive.listRecording(srcRecordingId, (c, l, d) -> {});
}

Try / catch

int attempts = 3;
while (attempts-- > 0)
{
    try
    {
        long replicationId = archive.replicate(srcRecordingId, replicationChannel, replayStreamId);
        break;
    }
    catch (io.aeron.exceptions.TimeoutException e)
    {
        if (attempts == 0) throw e;
        Thread.sleep(1000);
    }
}

Prevention

When it happens

Trigger: archiveProxy.replay(srcRecordingId, ...) returns false on every doWork() cycle for actionTimeoutMs while activeCorrelationId is NULL; typically because the source archive control-response/control-request publication is not connected, the media driver is down, or the UDP link between destination and source archive is down.

Common situations: Source archive process down or restarting; firewall/NAT dropping the control channel; replicationChannel misconfigured (wrong endpoint/port); source archive control session terminated mid-replication; severe network congestion keeping the control publication back-pressured for longer than the replication timeout; actionTimeoutMs set too low for a slow/high-latency WAN.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


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

Appendix: source

Thrown at aeron-archive/src/main/java/io/aeron/archive/ReplicationSession.java:731

                .fileIoMaxLength(fileIoMaxLength)
                .replayToken(replayToken);

            final ArchiveProxy archiveProxy = null != responseArchiveProxy ?
                responseArchiveProxy : srcArchive.archiveProxy();

            if (archiveProxy.replay(
                srcRecordingId,
                channelUri.toString(),
                replayStreamId,
                replayParams,
                correlationId,
                srcArchive.controlSessionId()))
            {
                workCount += trackAction(correlationId);
            }
            else if (epochClock.time() >= (timeOfLastActionMs + actionTimeoutMs))
            {
                throw new TimeoutException("failed to send replay request");
            }
        }
        else
        {
            final ControlResponsePoller poller = srcArchive.controlResponsePoller();
            workCount += poller.poll();

            if (hasResponse(poller))
            {
                srcReplaySessionId = poller.relevantId();
                state(State.AWAIT_IMAGE, "srcReplaySessionId=" + srcReplaySessionId);
            }
            else if (epochClock.time() >= (timeOfLastActionMs + actionTimeoutMs))
            {
                throw new TimeoutException("failed get acknowledgement of replay request to: " + replicationChannel);
            }
        }

View on GitHub (pinned to 6d60124e15)