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

  1. Await the future normally (`.await`) so the executor handles completion once.
  2. Use `futures::future::Fuse` / `is_terminated()` when polling manually.
  3. Stop the poll loop immediately upon receiving `Poll::Ready`.
  4. 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

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


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)