seanmonstar/warp · error
polled after complete
Error message
polled after complete
What it means
Internal invariant panic in the `OrElse` filter future. When the recovery future (`second.call(err)`) resolves, state becomes `State::Done` and the result is returned; polling afterwards is a `Future` contract violation with no recoverable value, so the library panics.
Solutions
- Use `.await` or a standard runtime (`tokio::spawn`) to drive the future.
- Wrap in `fuses::future::Fuse` and guard manual polls with `is_terminated()`.
- Ensure the poll loop breaks immediately on `Poll::Ready`.
- Ensure only one task/context polls a given future instance.
Example fix
// before let r1 = Pin::new(&mut fut).poll(cx)?; let r2 = Pin::new(&mut fut).poll(cx)?; // panic: polled after complete // after let out = filter.or_else(recover).await; // runtime drives once to completion
Defensive patterns
Strategy: try-catch
Validate before calling
use futures::future::FusedFuture; assert!(!fut.is_terminated(), "OrElse future already completed");
Type guard
fn alive<F: FusedFuture>(fut: &F) -> bool { !fut.is_terminated() } Try / catch
// panics aren't catchable; prevent structurally: let out = filter.or_else(recover).await; // runtime handles single-poll contract
Prevention
- Prefer async/await over manual poll loops.
- Add termination checks before every manual poll.
- Audit custom executors for re-poll-after-Ready bugs.
- Wrap futures in Fuse when stored in collections.
When it happens
Trigger: Polling the `OrElse` combinator future again after it returned `Poll::Ready(ex2)` from the recovery path — e.g. a poll loop that continues after `Ready` or an executor that re-dispatches completed tasks.
Common situations: Hand-written executors, misuse inside combinators like `join_all` with a future reused across iterations, wrappers lacking `FusedFuture` semantics, double wake handling.
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
- polled after complete
- polled after complete
- polled after complete
- polled after complete
- polled after complete
AI-assisted analysis of seanmonstar/warp@ff34d7213e (2026-09-09).
Data as JSON: /api/errors/13120bdedc28bf80.
Report an issue: GitHub.
Appendix: source
Thrown at src/filter/or_else.rs:96
type Output = Result<<F::Output as TryFuture>::Ok, <F::Output as TryFuture>::Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {
let pin = self.as_mut().project();
let (err, second) = match pin.state.project() {
StateProj::First(first, second) => match ready!(first.try_poll(cx)) {
Ok(ex) => return Poll::Ready(Ok(ex)),
Err(err) => (err, second),
},
StateProj::Second(second) => {
let ex2 = ready!(second.try_poll(cx));
self.set(OrElseFuture {
state: State::Done,
..*self
});
return Poll::Ready(ex2);
}
StateProj::Done => panic!("polled after complete"),
};
pin.original_path_index.reset_path();
let fut2 = second.call(err);
self.set(OrElseFuture {
state: State::Second(fut2),
..*self
});
}
}
}
View on GitHub (pinned to ff34d7213e)