{"record":{"id":"4b032da925ec8569","repo":"aeron-io/aeron","slug":"failed-get-acknowledgement-of-replay-request-to","errorCode":null,"errorMessage":"failed get acknowledgement of replay request to: \" + replicationChannel","messagePattern":"failed get acknowledgement of replay request to: \" \\+ replicationChannel","errorType":"exception","errorClass":"TimeoutException","httpStatus":null,"severity":"error","filePath":"aeron-archive/src/main/java/io/aeron/archive/ReplicationSession.java","lineNumber":746,"sourceCode":"            }\n            else if (epochClock.time() >= (timeOfLastActionMs + actionTimeoutMs))\n            {\n                throw new TimeoutException(\"failed to send replay request\");\n            }\n        }\n        else\n        {\n            final ControlResponsePoller poller = srcArchive.controlResponsePoller();\n            workCount += poller.poll();\n\n            if (hasResponse(poller))\n            {\n                srcReplaySessionId = poller.relevantId();\n                state(State.AWAIT_IMAGE, \"srcReplaySessionId=\" + srcReplaySessionId);\n            }\n            else if (epochClock.time() >= (timeOfLastActionMs + actionTimeoutMs))\n            {\n                throw new TimeoutException(\"failed get acknowledgement of replay request to: \" + replicationChannel);\n            }\n        }\n\n        return workCount;\n    }\n\n    private int awaitImage()\n    {\n        int workCount = 0;\n\n        final Image image = recordingSubscription.imageBySessionId((int)srcReplaySessionId);\n        if (null != image)\n        {\n            this.image = image;\n            state(null == liveDestination ? State.REPLICATE : State.CATCHUP,\n                \"image.correlationId=\" + image.correlationId() +\n                \", image.sessionId=\" + image.sessionId() +\n                \", image.joinPosition=\" + image.joinPosition());","sourceCodeStart":728,"sourceCodeEnd":764,"githubUrl":"https://github.com/aeron-io/aeron/blob/6d60124e15e35c11b49ba2e3c2c2858a09a18803/aeron-archive/src/main/java/io/aeron/archive/ReplicationSession.java#L728-L764","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: failing because response channel was unreachable\nAeronArchive.Configuration.configure(ctx)\n    .controlChannel(\"aeron:udp?endpoint=src-host:8010\")\n    // controlResponseChannel unreachable from source, default timeout\n// after: explicitly set a reachable response channel and longer timeout\nAeronArchive.Configuration.configure(ctx)\n    .controlChannel(\"aeron:udp?endpoint=src-host:8010\")\n    .controlResponseChannel(\"aeron:udp?endpoint=dst-host:0\")\n    .backupChannel(null);\nsystemProps.put(\"aeron.archive.replication.timeout\", \"60000000000\"); // 60s","handlingStrategy":"retry","validationCode":"// Ensure the control-response channel is bound and reachable before replicating\nfinal String responseChannel = archiveCtx.controlResponseChannel();\nif (null == responseChannel || responseChannel.contains(\":0\"))\n{\n    // wildcard is fine for binding but verify with a round-trip call first\n    archive.getRecordingPosition(dstRecordingId); // must return, not time out\n}","typeGuard":null,"tryCatchPattern":"try\n{\n    long id = archive.replicate(srcRecordingId, dstRecordingId, replicationChannel, replayStreamId);\n    // poll AeronArchive for replication progress instead of fire-and-forget\n}\ncatch (io.aeron.exceptions.TimeoutException e)\n{\n    LOGGER.error(\"source archive never ACKed replay request; check source conductor and response channel\", e);\n    throw e;\n}","preventionTips":["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"],"tags":["network","timeout","aeron-archive","control-response"],"backgroundTag":"request-timeout","analyzedSha":"6d60124e15e35c11b49ba2e3c2c2858a09a18803","analyzedAt":"2026-09-12T11:17:07.683Z","contentChangedAt":"2026-09-12T11:17:07.683Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}