seanmonstar/warp · error
polled after complete
Error message
polled after complete
What it means
Internal invariant panic in the `Or` (Either) filter future. After the error-handling branch completes and the future transitions to `State::Done`, any subsequent poll violates the `Future` contract; the library has no result left to hand out and panics.
Solutions
- Await the future normally (`.await`) so the executor handles completion once.
- Use `futures::future::Fuse` / `is_terminated()` when polling manually.
- Stop the poll loop immediately upon receiving `Poll::Ready`.
- Deduplicate polling: ensure one owner polls the future per wake-up.
Example fix
// before
if let Poll::Ready(v) = fut.poll(cx) { }
let _ = fut.poll(cx); // second poll after completion -> panic
// after
let mut fut = filter.or(fallback).fuse();
if !fut.is_terminated() {
let _ = fut.poll(cx);
} Defensive patterns
Strategy: try-catch
Validate before calling
use futures::future::FusedFuture;
fn poll_once_if_alive<F: FusedFuture + Unpin>(fut: &mut F, cx: &mut Context<'_>) {
if !fut.is_terminated() { let _ = fut.poll_unpin(cx); }
} Type guard
fn is_finished(fut: &(impl FusedFuture + ?Sized)) -> bool { fut.is_terminated() } Try / catch
// avoid double-poll entirely:
let mut fut = filter.or(fallback).fuse();
match fut.poll_unpin(cx) {
Poll::Ready(v) => return v,
Poll::Pending => {} // do not poll again until woken
} Prevention
- Only re-poll after a wake-up, never speculatively.
- Use Fuse/FusedFuture around manual polling.
- Drop futures once they complete.
- Avoid sharing one future between tasks.
When it happens
Trigger: Polling the `Or` combinator's `EitherFuture` after it returned `Poll::Ready` — the `StateProj::Done` match arm. Typically caused by a manual poll loop that doesn't stop at `Ready`, or polling from two places.
Common situations: Custom executors, incorrectly implemented `select`/`race` wrappers, storing futures in collections and re-polling finished entries, task frameworks that ignore `FusedFuture`.
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/6eb9f7b839c1b10a.
Report an issue: GitHub.
Appendix: source
Thrown at src/filter/or.rs:101
(e, second.filter(Internal))
}
},
StateProj::Second(err1, second) => {
let ex2 = match ready!(second.try_poll(cx)) {
Ok(ex2) => Ok((Either::B(ex2),)),
Err(e) => {
pin.original_path_index.reset_path();
let err1 = err1.take().expect("polled after complete");
Err(e.combine(err1))
}
};
self.set(EitherFuture {
state: State::Done,
..*self
});
return Poll::Ready(ex2);
}
StateProj::Done => panic!("polled after complete"),
};
self.set(EitherFuture {
state: State::Second(Some(err1), fut2),
..*self
});
}
}
}
View on GitHub (pinned to ff34d7213e)