vectordotdev/vector · critical

Maximum requests in-flight; poll_ready must be called first

Error message

Maximum requests in-flight; poll_ready must be called first

What it means

This panic fires in AdaptiveConcurrencyService::call when call() is invoked without a previously acquired concurrency permit. The Tower Service contract requires poll_ready() to return Poll::Ready(Ok(())) before call(); the state machine stores the permit in State::Ready, and call() consumed a state that was not Ready. This is an internal invariant violation of the Tower service protocol, not a user-facing condition.

Solutions

  1. Ensure every call() is preceded by poll_ready() returning Ready(Ok(())) per the Tower contract
  2. Re-poll poll_ready() after each call() since the service returns to NotReady while a request is in flight
  3. Route the service through tower::buffer::Buffer, which handles the ready/call protocol automatically

Example fix

// before
let fut = service.call(req);

// after
ready!(service.poll_ready(cx))?;
let fut = service.call(req);
Defensive patterns

Strategy: type-guard

Validate before calling

// Before calling, assert readiness
assert!(matches!(svc.poll_ready(PollCtx), Poll::Ready(Ok(()))));

Type guard

fn is_ready(state: &State) -> bool { matches!(state, State::Ready(_)) }

Prevention

When it happens

Trigger: Calling the service without a preceding poll_ready()==Ready; calling call() twice without re-polling poll_ready() after the first request; a custom Tower layer or buffer that bypasses poll_ready.

Common situations: Custom sink code or third-party Tower middleware wrapping Vector's adaptive-concurrency service incorrectly; regressing during sink refactors where the ready/call handshake is broken.

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


AI-assisted analysis of vectordotdev/vector@bdb87aeaa4 (2026-09-16). Data as JSON: /api/errors/c7b7d3a3a0085e39. Report an issue: GitHub.

Appendix: source

Thrown at src/sinks/util/adaptive_concurrency/service.rs:77

            self.state = match self.state {
                State::Ready(_) => return self.inner.poll_ready(cx).map_err(Into::into),
                State::Waiting(ref mut fut) => {
                    tokio::pin!(fut);
                    let permit = ready!(fut.poll(cx));
                    State::Ready(permit)
                }
                State::Empty => State::Waiting(Box::pin(Arc::clone(&self.controller).acquire())),
            };
        }
    }

    fn call(&mut self, request: Request) -> Self::Future {
        // Make sure a permit has been acquired
        let permit = match mem::replace(&mut self.state, State::Empty) {
            // Take the permit.
            State::Ready(permit) => permit,
            // whoopsie!
            _ => panic!("Maximum requests in-flight; poll_ready must be called first"),
        };

        self.controller.start_request();

        // Call the inner service
        let future = self.inner.call(request);

        ResponseFuture::new(future, permit, Arc::clone(&self.controller))
    }
}

impl<S, L> Load for AdaptiveConcurrencyLimit<S, L> {
    type Metric = f64;

    fn load(&self) -> Self::Metric {
        self.controller.load()
    }
}

View on GitHub (pinned to bdb87aeaa4)