vectordotdev/vector · error

poll_ready must be called first

Error message

poll_ready must be called first

What it means

NetworkService implements tower::Service for sink connections: poll_ready drives connect/reconnect and leaves the service in NetworkServiceState::Connected; call() then takes the connected socket out of that state. call() invoked while the state is Connecting or otherwise not Connected panics with 'poll_ready must be called first' - it enforces the tower Service contract that call may only follow a poll_ready that returned Poll::Ready(Ok(())).

Source

Thrown at src/sinks/util/service/net/mod.rs:333

                        Ok(maybe_socket) => match maybe_socket {
                            Some(socket) => NetworkServiceState::Connected(socket),
                            None => NetworkServiceState::Disconnected,
                        },
                        Err(_) => return Poll::Ready(Err(NetError::ServiceSocketChannelClosed)),
                    }
                }
            };
        }
        Poll::Ready(Ok(()))
    }

    fn call(&mut self, buf: Vec<u8>) -> Self::Future {
        let (tx, rx) = oneshot::channel();

        let mut socket = match std::mem::replace(&mut self.state, NetworkServiceState::Sending(rx))
        {
            NetworkServiceState::Connected(socket) => socket,
            _ => panic!("poll_ready must be called first"),
        };

        Box::pin(async move {
            match socket.send(&buf).await.context(net_error::FailedToSend) {
                Ok(sent) => {
                    // Emit an error if we weren't able to send the entire buffer.
                    if sent != buf.len() {
                        socket.on_partial_send(buf.len(), sent);
                    }

                    // Send the socket back to the service, since theoretically it's still valid to
                    // reuse given that we may have simply overrun the OS socket buffers, etc.
                    tx.send(Some(socket)).ok();

                    Ok(sent)
                }
                Err(e) => {
                    // We need to signal back to the service that it needs to create a fresh socket

View on GitHub (pinned to 3708c39b12)

Solutions

  1. Use tower::ServiceExt::ready() before every call: let mut svc = svc.ready().await?; then svc.call(buf)
  2. Audit custom Service middleware to confirm it propagates poll_ready and never calls the inner service while Pending
  3. Ensure a future returned from a Pending poll_ready is polled to completion (the Connecting state must finish) before call()
  4. Add a unit test that drives poll_ready/call in order using tower's test utilities

Example fix

// before
let fut = service.call(buf); // panics: state is Connecting, poll_ready never returned Ready

// after
use tower::ServiceExt;
let mut service = service.ready().await?; // poll_ready -> Ready(Ok(()))
let fut = service.call(buf);
Defensive patterns

Strategy: validation

Validate before calling

// Enforce the readiness precondition before every call:
match service.poll_ready(cx) {
    Poll::Ready(Ok(())) => { let fut = service.call(buf); /* ok */ }
    Poll::Ready(Err(e)) => { /* surface connection failure */ }
    Poll::Pending => { /* park on the waker; calling now would panic */ }
}

Try / catch

// Last resort - the panic is synchronous inside call():
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| service.call(buf)));
// Treat Err(payload) as a programming error and fix the driver protocol instead of shipping this

Prevention

When it happens

Trigger: Calling service.call(buf) on NetworkService (or a wrapper around it) without a prior poll_ready that returned Ready; calling again while the Connecting future from a Pending poll_ready is still outstanding; custom middleware that forwards call() without checking inner readiness.

Common situations: Writing custom tower middleware or a hand-rolled sink driver around Vector's internal sink service; porting code from older tower 0.3 ready_and() patterns; polling loops that skip the readiness step under load.

Related errors


AI-assisted analysis of vectordotdev/vector@3708c39b12 (2026-08-20). Data as JSON: /api/errors/5c04efda4bfd018d. Report an issue: GitHub.