aeron-io/aeron · error · IllegalArgumentException
Subscription URI must have 'control-mode=manual' uri=
Error message
Subscription URI must have 'control-mode=manual' uri=<channel>
What it means
ReplayMerge requires the subscription's channel URI to contain 'control-mode=manual' so the merge can control when the image joins the live destination. If the subscription URI lacks this parameter, the constructor throws IllegalArgumentException with the offending URI.
Solutions
- Add 'control-mode=manual' to the subscription URI before creating the subscription and ReplayMerge
- Create a fresh subscription with the corrected URI rather than reusing an auto-control-mode one
- Verify the URI with ChannelUri to confirm the parameter is present
Example fix
// before
Subscription sub = aeron.addSubscription("aeron:udp?endpoint=0.0.0.0:2000", streamId);
// after
Subscription sub = aeron.addSubscription("aeron:udp?endpoint=0.0.0.0:2000|control-mode=manual", streamId); Defensive patterns
Strategy: validation
Validate before calling
if (!sub.channel().contains("control-mode=manual")) {
throw new IllegalArgumentException("subscription URI must contain control-mode=manual: " + sub.channel());
} Try / catch
try { new ReplayMerge(archive, sub, replayChannel, replayDest, liveDest, recordingId, 0); }
catch (IllegalArgumentException e) { if (e.getMessage().contains("control-mode=manual")) { recreateSubscriptionWithManualControlMode(); } } Prevention
- Add control-mode=manual whenever creating subscriptions intended for ReplayMerge
- Check URI parameters with ChannelUri before constructing the merge
- Centralize subscription creation in one helper that enforces the parameter
When it happens
Trigger: Calling new ReplayMerge(...) with a subscription created from a URI missing 'control-mode=manual', e.g. 'aeron:udp?endpoint=host:port'.
Common situations: Reusing an existing subscription that used automatic control mode; forgetting that ReplayMerge needs manual MDC control; older samples/docs showing plain endpoints.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- IPC merging is not supported
- Unable to derive…
- unsupported unicast flow control strategy: fc=
- invalid channel
- publication-window-length=
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/7c4264b9b6a6c37c.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-archive/src/main/java/io/aeron/archive/client/ReplayMerge.java:133
final String replayChannel,
final String replayDestination,
final String liveDestination,
final long recordingId,
final long startPosition,
final EpochClock epochClock,
final long mergeProgressTimeoutMs)
{
if (subscription.channel().startsWith(IPC_CHANNEL) ||
replayChannel.startsWith(IPC_CHANNEL) ||
replayDestination.startsWith(IPC_CHANNEL) ||
liveDestination.startsWith(IPC_CHANNEL))
{
throw new IllegalArgumentException("IPC merging is not supported");
}
if (!subscription.channel().contains("control-mode=manual"))
{
throw new IllegalArgumentException(
"Subscription URI must have 'control-mode=manual' uri=" + subscription.channel());
}
this.archive = archive;
this.subscription = subscription;
this.epochClock = epochClock;
this.replayDestination = replayDestination;
this.liveDestination = liveDestination;
this.recordingId = recordingId;
this.startPosition = startPosition;
this.mergeProgressTimeoutMs = mergeProgressTimeoutMs;
replayChannelUri = ChannelUri.parse(replayChannel);
replayChannelUri.put(CommonContext.LINGER_PARAM_NAME, "0");
replayChannelUri.put(CommonContext.EOS_PARAM_NAME, "false");
final String replayEndpoint = ChannelUri.parse(replayDestination).get(ENDPOINT_PARAM_NAME);
if (replayEndpoint.endsWith(":0"))View on GitHub (pinned to 6d60124e15)