elastic/elasticsearch · error · IllegalStateException

sniffer can only be set once

Error message

sniffer can only be set once

What it means

SniffOnFailureListener.setSniffer may be called at most once (guarded by an AtomicBoolean). A second call raises IllegalStateException because the listener is designed to be bound to exactly one Sniffer for its lifetime.

Source

Thrown at client/sniffer/src/main/java/org/elasticsearch/client/sniff/SniffOnFailureListener.java:52

public class SniffOnFailureListener extends RestClient.FailureListener {

    private volatile Sniffer sniffer;
    private final AtomicBoolean set;

    public SniffOnFailureListener() {
        this.set = new AtomicBoolean(false);
    }

    /**
     * Sets the {@link Sniffer} instance used to perform sniffing
     * @throws IllegalStateException if the sniffer was already set, as it can only be set once
     */
    public void setSniffer(Sniffer sniffer) {
        Objects.requireNonNull(sniffer, "sniffer must not be null");
        if (set.compareAndSet(false, true)) {
            this.sniffer = sniffer;
        } else {
            throw new IllegalStateException("sniffer can only be set once");
        }
    }

    @Override
    public void onFailure(Node node) {
        if (sniffer == null) {
            throw new IllegalStateException("sniffer was not set, unable to sniff on failure");
        }
        sniffer.sniffOnFailure();
    }
}

View on GitHub (pinned to db6a809a66)

Solutions

  1. Create a fresh SniffOnFailureListener for each new Sniffer rather than reusing the old one.
  2. Track the current listener and reset it together with the Sniffer and RestClient on reconfiguration.
  3. If you need to swap sniffers, also swap the listener (it is cheap).

Example fix

// before
SniffOnFailureListener listener = new SniffOnFailureListener();
client1 = ...; listener.setSniffer(Sniffer.builder(client1).build());
client2 = ...; listener.setSniffer(Sniffer.builder(client2).build()); // throws
// after
SniffOnFailureListener listener = new SniffOnFailureListener();
client2 = ...; listener.setSniffer(Sniffer.builder(client2).build()); // new listener per client
Defensive patterns

Strategy: validation

Validate before calling

if (listenerAlreadyBound) throw new IllegalStateException("create a new SniffOnFailureListener instead of rebinding");
listener.setSniffer(sniffer);

Prevention

When it happens

Trigger: Calling setSniffer(sniffer1) then setSniffer(sniffer2) on the same listener instance; re-binding during a reconfiguration hot-reload.

Common situations: Re-initialisation code that builds a new Sniffer on config change but reuses the same listener; multiple modules each trying to register their sniffer.

Related errors


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/2a341f14e8cb65e0. Report an issue: GitHub.