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
- Create a fresh SniffOnFailureListener for each new Sniffer rather than reusing the old one.
- Track the current listener and reset it together with the Sniffer and RestClient on reconfiguration.
- 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
- Treat SniffOnFailureListener as single-use: build a new one whenever you build a new Sniffer.
- Reset listener + sniffer + client together on reconfiguration.
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
- sniffer was not set, unable to sniff on failure
- Cannot add nodes to test cluster after is has been frozen
- Configuration for {} can not be altered, already locked
- sniffRequestTimeoutMillis must be greater than 0
- expected data to start with an object
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/2a341f14e8cb65e0.
Report an issue: GitHub.