{"record":{"id":"f4972b9c78872d31","repo":"seanmonstar/warp","slug":"polled-after-complete-f4972b","errorCode":null,"errorMessage":"polled after complete","messagePattern":"polled after complete","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/filter/then.rs","lineNumber":91,"sourceCode":"    F: Func<T::Ok>,\n    F::Output: Future + Send,\n{\n    type Output = Result<(<F::Output as Future>::Output,), T::Error>;\n\n    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        loop {\n            match self.as_mut().project() {\n                StateProj::First(first, second) => {\n                    let ex1 = ready!(first.try_poll(cx))?;\n                    let fut2 = second.call(ex1);\n                    self.set(State::Second(fut2));\n                }\n                StateProj::Second(second) => {\n                    let ex2 = (ready!(second.poll(cx)),);\n                    self.set(State::Done);\n                    return Poll::Ready(Ok(ex2));\n                }\n                StateProj::Done => panic!(\"polled after complete\"),\n            }\n        }\n    }\n}\n","sourceCodeStart":73,"sourceCodeEnd":96,"githubUrl":"https://github.com/seanmonstar/warp/blob/ff34d7213ed55ec342304aa7ff6ac4b351da9e66/src/filter/then.rs#L73-L96","documentation":"Internal invariant panic in the `Then` filter future. After the sequenced future (`second`) resolves and `State::Done` is set with the result returned, re-polling violates the `Future` contract; the library panics as no result remains.","triggerScenarios":"Polling the `Then` combinator future after it already returned `Poll::Ready(Ok(ex2))` — typically from a poll loop that doesn't stop at completion or an executor that re-runs finished tasks.","commonSituations":"Custom executors, wrappers missing `FusedFuture`, calling `poll` both from a waker-driven loop and an outer select, retry logic that re-polls consumed futures.","solutions":["Use `.await`/`tokio::spawn` to drive the future once to completion.","Wrap with `futures::future::Fuse` and check `is_terminated()` before polling.","Return from the poll loop immediately when `Poll::Ready` is seen.","Ensure a single poller owns the future."],"exampleFix":"// before\nlet mut fut = filter.then(next);\nlet _ = fut.as_mut().poll(cx);\nlet _ = fut.as_mut().poll(cx); // panic: polled after complete\n\n// after\nlet out = filter.then(next).await; // no manual polls","handlingStrategy":"try-catch","validationCode":"use futures::future::FusedFuture;\nfn poll_guard<F: FusedFuture + Unpin>(fut: &mut F, cx: &mut Context<'_>) -> Option<F::Output> {\n    if fut.is_terminated() { return None; }\n    match fut.poll_unpin(cx) { Poll::Ready(v) => Some(v), _ => None }\n}","typeGuard":"fn not_done<F: FusedFuture>(fut: &F) -> bool { !fut.is_terminated() }","tryCatchPattern":"// structural prevention:\nlet out = filter.then(next).await; // no manual polls, no double poll","preventionTips":["Drive futures only via async/await or tokio::spawn.","Use Fuse and check `is_terminated()` for manual polling.","Ensure retry logic recreates futures rather than re-polling them.","Keep a single poller per future."],"tags":["panic","future","internal-invariant","rust","poll"],"backgroundTag":"invalid-state-transition","analyzedSha":"ff34d7213ed55ec342304aa7ff6ac4b351da9e66","analyzedAt":"2026-09-09T16:57:46.316Z","contentChangedAt":"2026-09-09T16:57:46.316Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}