vectordotdev/vector · info
Should never end
Error message
Should never end
What it means
This panic fires in the health-check service's `poll_ready` when an endpoint fails probation and the circuit opens, but `backoff.next()` returns `None`. Vector's `ExponentialBackoff` is configured with `max_attempts: None` (unlimited), so the iterator never ends; the expect asserts that invariant.
Solutions
- Report as a bug to Vector if observed.
- Verify no custom build constrains the health backoff's max attempts.
Defensive patterns
Strategy: fallback
Prevention
- Do not patch the health service to use a bounded backoff.
- Report occurrences to Vector maintainers with logs of the probation transition.
- Monitor endpoint health to reduce circuit-open transitions.
When it happens
Trigger: An endpoint fails its health probation period and the service transitions to `CircuitState::Open`; panic requires the backoff iterator to be exhausted, which the unlimited-attempts configuration prevents.
Common situations: Not reachable with default configuration; could only appear if the health service's backoff were constructed with a bounded attempt count.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- backoff never ends
- a record with a next ID must have an event count
- a valid HTTP/1 URI is valid as an HTTP URI
- a validated HTTP endpoint is a valid `http 1` URI
- all json-file keys should be matched
AI-assisted analysis of vectordotdev/vector@bdb87aeaa4 (2026-09-16).
Data as JSON: /api/errors/24c6992d74224bd4.
Report an issue: GitHub.
Appendix: source
Thrown at src/sinks/util/service/health.rs:182
CircuitState::HalfOpen {
permit: None,
ref mut done,
} => {
let done = Pin::new(done);
ready!(done.poll(cx));
if self.counters.healthy(self.snapshot).is_ok() {
// A healthy response was observed
info!(message = "Endpoint is healthy.", endpoint = %&self.endpoint);
self.backoff.reset();
self.open.clone().open(emit_active_endpoints);
CircuitState::Closed
} else {
debug!(message = "Endpoint failed probation.", endpoint = %&self.endpoint);
CircuitState::Open(
sleep(self.backoff.next().expect("Should never end")).boxed(),
)
}
}
CircuitState::Closed => {
// Check for errors
match self.counters.healthy(self.snapshot) {
Ok(snapshot) => {
// Healthy
self.snapshot = snapshot;
return self.inner.poll_ready(cx).map_err(Into::into);
}
Err(errors) if errors >= UNHEALTHY_AMOUNT_OF_ERRORS => {
// Unhealthy
warn!(message = "Endpoint is unhealthy.", endpoint = %&self.endpoint);
CircuitState::Open(
sleep(self.backoff.next().expect("Should never end")).boxed(),
)
}View on GitHub (pinned to bdb87aeaa4)