vectordotdev/vector · error
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
AdaptiveConcurrencyLimit implements tower::Service with a permit state machine: poll_ready acquires a concurrency permit (via the controller semaphore) and stores it in State::Ready; call consumes that permit via mem::replace. Calling call() when no permit is stored - state Empty or Waiting - panics with 'Maximum requests in-flight; poll_ready must be called first'. This enforces the tower Service contract that call may only follow a Ready poll_ready, one call per readiness.
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 3708c39b12)
Solutions
- Await readiness before every call: let mut svc = svc.ready().await?; then svc.call(req)
- If wrapping the service, delegate poll_ready to the inner AdaptiveConcurrencyLimit and never call inner.call unless your own poll_ready returned Ready
- Audit for loops that cache a ready &mut service across multiple call invocations
- If hit inside Vector's own pipeline (no custom code), upgrade and report - it violates an internal contract
Example fix
// before let fut = svc.call(request); // no readiness -> panic // after let mut svc = svc.ready().await?; let fut = svc.call(request);
Defensive patterns
Strategy: validation
Validate before calling
// tower contract: acquire readiness before each call use tower::ServiceExt; let mut svc = svc.ready().await?; // poll_ready -> Ready let response = svc.call(request).await?;
Prevention
- Always await ready()/poll_ready before every call - one call per readiness acquisition
- Never cache a ready service reference across multiple call invocations
- When wrapping services, forward poll_ready to the inner service and preserve the contract
- Use tower's combinators (ServiceBuilder, buffered, etc.) rather than hand-driving calls
When it happens
Trigger: Issuing svc.call(request) without a preceding successful poll_ready/ready().await - e.g. calling twice after a single readiness, driving calls in a loop without re-checking readiness, or a wrapper service that forwards call but not poll_ready. With Vector's adaptive_concurrency retries/sinks settings, an internal violation would be a Vector bug.
Common situations: Custom sinks or middlewares wrapping Vector's service stack that cache a ready service and reuse it for multiple calls; misuse of tower APIs (svc.call instead of ServiceExt::ready().await + call); batching/partitioning layers that issue multiple requests per readiness slot.
Related errors
- poll_ready must be called first
- path and query should never fail to parse
- Serializer does not support JSON
- Paths must always start with a leading forward slash (`/`).
- Only leaf nodes should be allowed to be non-object values.
AI-assisted analysis of vectordotdev/vector@3708c39b12 (2026-08-20).
Data as JSON: /api/errors/c7b7d3a3a0085e39.
Report an issue: GitHub.