aeron-io/aeron · error · TimeoutException
failed to send recording position request
Error message
failed to send recording position request
What it means
Thrown by ReplicationSession.srcRecordingPosition when the archive proxy cannot even enqueue the getRecordingPosition control request to the source archive within actionTimeoutMs (archiveProxy().getRecordingPosition returned false repeatedly). The proxy offer fails when the control publication is not yet connected or its send buffer is full, and Aeron aborts the replication rather than spin forever. It signals the control channel to the source archive is not making progress.
Solutions
- Check the source archive is still running and the replication session is connected (no earlier REPLICATION_CONNECTION_FAILURE).
- Increase the replication action timeout in the Archive client context to tolerate control backpressure.
- Confirm the control channel network path is up (no firewall/NAT drops on long-lived UDP flows).
- Restart/redo the replication with AeronArchive.retract/replicate so a fresh control session is established.
- Upgrade Aeron if you hit known control-publication reconnection issues.
Example fix
// before ctx.actionTimeoutNs(TimeUnit.SECONDS.toNanos(5)); // after ctx.actionTimeoutNs(TimeUnit.SECONDS.toNanos(60));
Defensive patterns
Strategy: retry
Validate before calling
// ensure the control session to the source archive is live before replication
if (!aeronArchiveProxy.isConnected()) {
throw new IllegalStateException("source archive control channel not connected");
} Try / catch
try {
session.doWork();
} catch (TimeoutException e) {
if (e.getMessage().contains("failed to send recording position request")) {
log.error("control channel to source archive stalled; re-establishing replication");
restartReplication();
}
} Prevention
- Keep the source archive running for the life of the replication; monitor its process/conductor.
- Use a reliable control channel path (avoid lossy links for archive control traffic).
- Set generous actionTimeoutNs to absorb control backpressure.
- Detect source archive restarts and proactively re-create the replication session.
When it happens
Trigger: Replication session in the srcRecordingPosition state where srcArchive.archiveProxy().getRecordingPosition(...) keeps returning false — the source archive control response publication is not connected (session lost), the control ring buffer is backed up, or the retry window exceeds actionTimeoutMs.
Common situations: Source archive restarted mid-replication so the cached control session died; control publication to a dead endpoint; extreme control-message load on the source archive; overly tight replication timeouts on slow networks.
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 get recording position
- failed to fetch remote recording descriptor
- failed to list remote recording descriptor
- cannot live merge without active source recording
- failed to resolve subscription endpoint: channel=" +…
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/70039e6cd16e8dc5.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-archive/src/main/java/io/aeron/archive/ReplicationSession.java:498
return workCount;
}
private int srcRecordingPosition()
{
int workCount = 0;
if (NULL_VALUE == activeCorrelationId)
{
final long correlationId = aeron.nextCorrelationId();
final long controlSessionId = srcArchive.controlSessionId();
if (srcArchive.archiveProxy().getRecordingPosition(srcRecordingId, correlationId, controlSessionId))
{
workCount += trackAction(correlationId);
}
else if (epochClock.time() >= (timeOfLastActionMs + actionTimeoutMs))
{
throw new TimeoutException("failed to send recording position request");
}
}
else
{
final ControlResponsePoller poller = srcArchive.controlResponsePoller();
workCount += poller.poll();
if (hasResponse(poller))
{
srcRecordingPosition = poller.relevantId();
if (NULL_POSITION == srcRecordingPosition && null != liveDestination)
{
throw new ArchiveException("cannot live merge without active source recording");
}
state(State.EXTEND, "");
}
else if (epochClock.time() >= (timeOfLastActionMs + actionTimeoutMs))View on GitHub (pinned to 6d60124e15)