aeron-io/aeron · error · TimeoutException
failed get acknowledgement of replay request to: " +…
Error message
failed get acknowledgement of replay request to: " + replicationChannel
What it means
Thrown by ReplicationSession.replay() after the replay request was sent (activeCorrelationId != NULL) but the source archive's control response acknowledging the replay (carrying the replay session id) was never received within actionTimeoutMs. The session polls srcArchive.controlResponsePoller() each doWork() cycle; no matching OK response by the deadline means the source archive never processed or answered the replay request.
Solutions
- Inspect the source archive log for the replay request correlationId to see if it was received, rejected, or never arrived.
- Verify the control-response channel connectivity in BOTH directions — the source must be able to send responses to the destination archive's response subscription.
- Increase the replication action timeout (aeron.archive.replication.timeout) to cover source-archive load spikes.
- Check source archive health (CPU, archive catalog/disk I/O, recording log contention) — a stalled archive conductor delays the OK reply.
- If the source archive restarted, re-issue the replication; the old correlation id will never be answered.
Example fix
// before: failing because response channel was unreachable
AeronArchive.Configuration.configure(ctx)
.controlChannel("aeron:udp?endpoint=src-host:8010")
// controlResponseChannel unreachable from source, default timeout
// after: explicitly set a reachable response channel and longer timeout
AeronArchive.Configuration.configure(ctx)
.controlChannel("aeron:udp?endpoint=src-host:8010")
.controlResponseChannel("aeron:udp?endpoint=dst-host:0")
.backupChannel(null);
systemProps.put("aeron.archive.replication.timeout", "60000000000"); // 60s Defensive patterns
Strategy: retry
Validate before calling
// Ensure the control-response channel is bound and reachable before replicating
final String responseChannel = archiveCtx.controlResponseChannel();
if (null == responseChannel || responseChannel.contains(":0"))
{
// wildcard is fine for binding but verify with a round-trip call first
archive.getRecordingPosition(dstRecordingId); // must return, not time out
} Try / catch
try
{
long id = archive.replicate(srcRecordingId, dstRecordingId, replicationChannel, replayStreamId);
// poll AeronArchive for replication progress instead of fire-and-forget
}
catch (io.aeron.exceptions.TimeoutException e)
{
LOGGER.error("source archive never ACKed replay request; check source conductor and response channel", e);
throw e;
} Prevention
- Always configure an explicit, reachable controlResponseChannel so the source can reply
- Open firewall rules symmetrically for both control and control-response channels
- Watch source archive conductor latency; an overloaded conductor delays control responses
- Re-issue replication after any source archive restart
When it happens
Trigger: poller.poll() runs each cycle but hasResponse(poller) never becomes true for the outstanding replay correlationId before timeOfLastActionMs + actionTimeoutMs; the source archive got the request but dropped/failed it, the control response channel is broken, or the response is going to a different control-response channel than the poller subscribes to.
Common situations: Source archive overloaded or stalled (e.g. disk I/O saturation in the archive store) so it cannot service the replay; response publication not connected back to the replicating archive (asymmetric firewall — outbound OK, inbound blocked); source archive restarted and lost the correlation; timeout too aggressive for large/loaded archives.
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
- failed to send replay request
- failed get replay image for sessionId=" +…
- ControlSession.RESPONSE_NOT_CONNECTED_MSG + ": " + session
- failed to fetch remote recording descriptor
- (int)poller.relevantId()
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/4b032da925ec8569.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-archive/src/main/java/io/aeron/archive/ReplicationSession.java:746
}
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);
}
}
return workCount;
}
private int awaitImage()
{
int workCount = 0;
final Image image = recordingSubscription.imageBySessionId((int)srcReplaySessionId);
if (null != image)
{
this.image = image;
state(null == liveDestination ? State.REPLICATE : State.CATCHUP,
"image.correlationId=" + image.correlationId() +
", image.sessionId=" + image.sessionId() +
", image.joinPosition=" + image.joinPosition());View on GitHub (pinned to 6d60124e15)