aeron-io/aeron · error · AeronException

spies can not reject images

Error message

spies can not reject images

What it means

Aeron throws this from rejectImage() when the subscription's channel is a spy channel (starts with "aeron:spy"). Spy subscriptions read directly from a local publisher's shared memory rather than over the network, and the reject-image protocol (which tells a sender to stop sending) is only defined for real network subscriptions. Rejecting is not meaningful for a spy, so Aeron fails fast with AeronException.

Solutions

  1. Check subscription.channel().startsWith("aeron:spy") (Subscription.SPY_PREFIX) before wiring rejectImage and skip it for spies
  2. Enforce backpressure on spy consumers differently: slow the local publisher or stop polling, since rejection has no meaning in shared memory
  3. Use a network (udp/ipc non-spy) channel if the reject-image semantics are actually required
  4. Catch AeronException around rejectImage and fall back to simply not polling that image

Example fix

// before
image.rejectImage(correlationId, position, "not authorised");

// after
if (!subscription.channel().startsWith(Subscription.SPY_PREFIX))
{
    image.rejectImage(correlationId, position, "not authorised");
}
else
{
    // handle spy channel another way, e.g. stop polling
}
Defensive patterns

Strategy: validation

Validate before calling

if (subscription.channel().startsWith("aeron:spy")) { throw new UnsupportedOperationException("rejectImage not supported on spy subscriptions"); }

Type guard

boolean supportsRejectImage(Subscription s) { return !s.channel().startsWith(Subscription.SPY_PREFIX); }

Try / catch

try { image.rejectImage(correlationId, position, reason); } catch (AeronException e) { if (e.getMessage().contains("spies")) { applyAlternativeBackpressure(image); } else { throw e; } }

Prevention

When it happens

Trigger: Calling image.rejectImage(correlationId, position, reason) (or subscription.rejectImage) on an Image obtained from a Subscription whose channel is "aeron:spy?...".

Common situations: Backpressure/abuse-control logic (e.g. rejecting a slow or unauthorized publisher) written generically for all subscriptions but deployed against an inter-process (IPC-over-shared-memory) spy channel in the same JVM/host; copy-pasted channel configs switching from udp to spy.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at aeron-client/src/main/java/io/aeron/Subscription.java:638

            i++;
        }

        if (null != removedImage)
        {
            removedImage.close();
            images = oldArray.length == 1 ? EMPTY_IMAGES : ArrayUtil.remove(oldArray, i);
            conductor.releaseLogBuffers(removedImage.logBuffers(), correlationId, NULL_VALUE);
        }

        return removedImage;
    }

    void rejectImage(final long correlationId, final long position, final String reason)
    {
        if (channel().startsWith(SPY_PREFIX))
        {
            throw new AeronException("spies can not reject images");
        }

        conductor.rejectImage(correlationId, position, reason);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString()
    {
        return "Subscription{" +
            "registrationId=" + registrationId +
            ", isClosed=" + isClosed +
            ", isConnected=" + isConnected() +
            ", streamId=" + streamId +
            ", channel='" + channel + '\'' +
            ", localSocketAddresses=" + localSocketAddresses() +

View on GitHub (pinned to 6d60124e15)