aeron-io/aeron · error · InvalidChannelException
Aeron spies are invalid as send destinations: channel=
Error message
Aeron spies are invalid as send destinations: channel={destinationUri} What it means
An Aeron "spy" channel (URI prefixed with "aeron-spy:") is a virtual channel that taps traffic from an existing network publication via local shared memory. Spies can only be used for subscriptions; using one as a send/publication destination is invalid and rejected with InvalidChannelException.
Solutions
- Use a plain "aeron:udp?..." URI for send destinations instead of a spy prefix.
- Only use aeron-spy URIs with addSubscription() to locally tap an existing publication.
- Validate destination URIs in your code, rejecting any prefix other than "aeron:" for sends.
Example fix
// before
publication.addDestination("aeron-spy:udp?endpoint=localhost:40456");
// after
publication.addDestination("aeron:udp?endpoint=localhost:40456"); Defensive patterns
Strategy: validation
Validate before calling
ChannelUri uri = ChannelUri.parse(destination);
if ("aeron-spy".equals(uri.prefix())) throw new IllegalArgumentException("spy cannot be a send destination"); Type guard
boolean isSendableDestination(ChannelUri uri) {
return !"aeron-spy".equals(uri.prefix());
} Try / catch
try { publication.addDestination(destination); }
catch (InvalidChannelException e) { if (e.getMessage().contains("spies are invalid as send destinations")) { /* replace with aeron:udp URI */ } else throw e; } Prevention
- Restrict aeron-spy prefixes to subscription code paths
- Filter destination lists to "aeron:" prefixes before adding
- Document spy semantics as receive-only in team guidelines
When it happens
Trigger: Adding a spy URI as a publication or send destination, e.g. ChannelUri with prefix "aeron-spy:" passed to addPublication(), addDestination(), or MDC manual destination lists.
Common situations: Confusing spy subscriptions with spy publications; building dynamic destination lists where a spy URI sneaks in; misunderstanding that spy is receive-only local tapping.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- AeronArchive.Context.messageRetryAttempts must be > 0, got:
- applicationSpecificFeedback length must be equal to
- clientLivenessTimeoutNs=
- control has port=0 for subscription: channel=
- control-mode=response was specified, but no…
AI-assisted analysis of aeron-io/aeron@6d60124e15 (2026-09-12).
Data as JSON: /api/errors/001e5d22aeb2c797.
Report an issue: GitHub.
Appendix: source
Thrown at aeron-driver/src/main/java/io/aeron/driver/DriverConductor.java:2375
udpChannel.originalUriString());
}
}
private static void validateTimestampConfiguration(final UdpChannel udpChannel)
{
if (null != udpChannel.channelUri().get(MEDIA_RCV_TIMESTAMP_OFFSET_PARAM_NAME))
{
throw new InvalidChannelException(
"Media timestamps '" + MEDIA_RCV_TIMESTAMP_OFFSET_PARAM_NAME +
"' are not supported in the Java driver: channel=" + udpChannel.originalUriString());
}
}
private static void validateDestinationUri(final ChannelUri uri, final String destinationUri)
{
if (SPY_QUALIFIER.equals(uri.prefix()))
{
throw new InvalidChannelException("Aeron spies are invalid as send destinations: channel=" +
destinationUri);
}
for (final String invalidKey : INVALID_DESTINATION_KEYS)
{
if (uri.containsKey(invalidKey))
{
throw new InvalidChannelException(
"destinations must not contain the key: " + invalidKey + " channel=" + destinationUri);
}
}
if (Objects.equals(CONTROL_MODE_RESPONSE, uri.get(MDC_CONTROL_MODE_PARAM_NAME)))
{
throw new InvalidChannelException("destinations may not specify " +
MDC_CONTROL_MODE_PARAM_NAME + "=" + CONTROL_MODE_RESPONSE);
}
}View on GitHub (pinned to 6d60124e15)