aeron-io/aeron · error · TimeoutException

failed to resolve subscription endpoint: channel=" +…

Error message

failed to resolve subscription endpoint: channel=" + recordingSubscription.channel()

What it means

Thrown by ReplicationSession.replay when the destination-side recording subscription exists but Subscription.resolvedEndpoint() is still null after actionTimeoutMs — the transport never bound the subscription to a concrete local address (typically with dynamic/multi-destination or wildcard ports). Aeron fails the replication because the source archive cannot be told a replay destination without a resolved endpoint. The exception message includes the subscription's channel URI for diagnosis.

Solutions

  1. Check the replication channel URI: provide a concrete endpoint (or correct MDC control/response params) instead of a wildcard that never resolves.
  2. Verify with the OS (ss/netstat) that the subscription socket actually bound; look for firewall/SELinux blocks on the UDP port.
  3. Ensure MDC control-mode and control/response addresses are consistent when using multicast live merges.
  4. Increase the action timeout if endpoint resolution is merely slow in your environment.
  5. Test connectivity with a simple Aeron subscriber on the same channel URI to confirm it resolves an endpoint.

Example fix

// before: wildcard endpoint that never resolves
AeronArchive.replicate(archive, srcRecordingId, "aeron:udp?endpoint=0.0.0.0:0", streamId, srcCtl, srcRep);
// after: explicit endpoint
AeronArchive.replicate(archive, srcRecordingId, "aeron:udp?endpoint=10.1.2.3:40456", streamId, srcCtl, srcRep);
Defensive patterns

Strategy: try-catch

Validate before calling

// resolve the channel first and confirm a concrete endpoint exists
ChannelUri uri = ChannelUri.parse(replicationChannel);
String endpoint = uri.get(CommonContext.ENDPOINT_PARAM_NAME);
if (endpoint == null && !uri.hasControlModeResponse()) {
    throw new IllegalArgumentException("replication channel needs an endpoint or MDC control-mode=response");
}

Type guard

boolean endpointResolved(Subscription sub) {
    return sub != null && sub.isConnected() && sub.resolvedEndpoint() != null;
}

Try / catch

try {
    runReplication();
} catch (TimeoutException e) {
    if (e.getMessage().contains("failed to resolve subscription endpoint")) {
        log.error("subscription never resolved on channel {}; check endpoint/firewall/MDC config", channel);
        abortReplication();
    }
}

Prevention

When it happens

Trigger: Replication using multicast or MDC/manual multicast with a wildcard (0.0.0.0:0 style) endpoint where the subscription never resolves; firewall blocking the bind; destination URI missing/incorrect endpoint or control params so no address is ever resolved; OS-level failure to bind the socket within the timeout.

Common situations: Wildcard endpoints combined with control-mode=manual misconfigurations; cloud/firewall setups blocking UDP binds or MDC control traffic; typos in the replication channel URI (missing endpoint attribute); host lacking a routable interface so resolution never completes.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


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

Appendix: source

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

        responseArchiveProxy = new ArchiveProxy(responsePublication);
        state(State.REPLAY, "ArchiveProxy created");

        return workCount;
    }

    private int replay()
    {
        int workCount = 0;

        if (NULL_VALUE == activeCorrelationId)
        {
            final String resolvedEndpoint = recordingSubscription.resolvedEndpoint();
            if (null == resolvedEndpoint)
            {
                if (epochClock.time() >= (timeOfLastActionMs + actionTimeoutMs))
                {
                    throw new TimeoutException(
                        "failed to resolve subscription endpoint: channel=" + recordingSubscription.channel());
                }

                return workCount;
            }

            final ChannelUri channelUri = ChannelUri.parse(replicationChannel);
            final String endpoint = channelUri.get(CommonContext.ENDPOINT_PARAM_NAME);
            if (null != endpoint)
            {
                channelUri.replaceEndpointWildcardPort(resolvedEndpoint);
            }

            if (null != liveDestination)
            {
                channelUri.put(CommonContext.LINGER_PARAM_NAME, "0");
                channelUri.put(CommonContext.EOS_PARAM_NAME, "false");
            }

View on GitHub (pinned to 6d60124e15)