seanmonstar/warp · error

polled after complete

Error message

polled after complete

What it means

Internal invariant panic in the `AndThen` filter's combinator future. Once the chained future completes and the result is returned, the state machine moves to `State::Done`; polling after that violates the `Future` contract and there is no value to return, so the library panics.

Solutions

  1. Drive the filter future with `.await` or `tokio::spawn` rather than manual `poll` calls.
  2. Wrap the future with `futures::future::Fuse` and check `is_terminated()` before polling.
  3. Never poll a future again after it yields `Poll::Ready`; drop it instead.
  4. Review custom executors/wrappers for double-polling logic.

Example fix

// before
loop {
    let _ = Pin::new(&mut fut).poll(cx); // keeps polling after Ready -> panic
}

// after
use futures::future::FusedFuture;
let mut fut = filter.and_then(svc).fuse();
while !fut.is_terminated() {
    let _ = futures::poll!(fut.as_mut());
}
Defensive patterns

Strategy: try-catch

Validate before calling

use futures::future::FusedFuture;
if fut.is_terminated() { return; } // skip poll after completion

Type guard

fn can_poll<F: FusedFuture>(fut: &F) -> bool { !fut.is_terminated() }

Try / catch

// panic is not recoverable; avoid by construction:
let mut fut = filter.and_then(svc).fuse();
if !fut.is_terminated() { let _ = fut.poll_unpin(cx); }

Prevention

When it happens

Trigger: Polling the `AndThen` combinator future after it has returned `Poll::Ready` (the `StateProj::Done` branch) — caused by a custom executor re-polling a completed future or a wrapper that ignores `is_terminated`.

Common situations: Hand-rolled executors, select!/timeout wrappers written incorrectly, reusing a consumed future from a cache, or combining raw futures without `Fuse`.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of seanmonstar/warp@ff34d7213e (2026-09-09). Data as JSON: /api/errors/a4bd2c1556831918. Report an issue: GitHub.

Appendix: source

Thrown at src/filter/and_then.rs:106

    >;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        loop {
            match self.as_mut().project() {
                StateProj::First(first, second) => {
                    let ex1 = ready!(first.try_poll(cx))?;
                    let fut2 = second.call(ex1);
                    self.set(State::Second(fut2));
                }
                StateProj::Second(second) => {
                    let ex2 = match ready!(second.try_poll(cx)) {
                        Ok(item) => Ok((item,)),
                        Err(err) => Err(From::from(err)),
                    };
                    self.set(State::Done);
                    return Poll::Ready(ex2);
                }
                StateProj::Done => panic!("polled after complete"),
            }
        }
    }
}

View on GitHub (pinned to ff34d7213e)