{"record":{"id":"6eb9f7b839c1b10a","repo":"seanmonstar/warp","slug":"polled-after-complete-6eb9f7","errorCode":null,"errorMessage":"polled after complete","messagePattern":"polled after complete","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/filter/or.rs","lineNumber":101,"sourceCode":"                        (e, second.filter(Internal))\n                    }\n                },\n                StateProj::Second(err1, second) => {\n                    let ex2 = match ready!(second.try_poll(cx)) {\n                        Ok(ex2) => Ok((Either::B(ex2),)),\n                        Err(e) => {\n                            pin.original_path_index.reset_path();\n                            let err1 = err1.take().expect(\"polled after complete\");\n                            Err(e.combine(err1))\n                        }\n                    };\n                    self.set(EitherFuture {\n                        state: State::Done,\n                        ..*self\n                    });\n                    return Poll::Ready(ex2);\n                }\n                StateProj::Done => panic!(\"polled after complete\"),\n            };\n\n            self.set(EitherFuture {\n                state: State::Second(Some(err1), fut2),\n                ..*self\n            });\n        }\n    }\n}\n","sourceCodeStart":83,"sourceCodeEnd":111,"githubUrl":"https://github.com/seanmonstar/warp/blob/ff34d7213ed55ec342304aa7ff6ac4b351da9e66/src/filter/or.rs#L83-L111","documentation":"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.","triggerScenarios":"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.","commonSituations":"Custom executors, incorrectly implemented `select`/`race` wrappers, storing futures in collections and re-polling finished entries, task frameworks that ignore `FusedFuture`.","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."],"exampleFix":"// before\nif let Poll::Ready(v) = fut.poll(cx) { }\nlet _ = fut.poll(cx); // second poll after completion -> panic\n\n// after\nlet mut fut = filter.or(fallback).fuse();\nif !fut.is_terminated() {\n    let _ = fut.poll(cx);\n}","handlingStrategy":"try-catch","validationCode":"use futures::future::FusedFuture;\nfn poll_once_if_alive<F: FusedFuture + Unpin>(fut: &mut F, cx: &mut Context<'_>) {\n    if !fut.is_terminated() { let _ = fut.poll_unpin(cx); }\n}","typeGuard":"fn is_finished(fut: &(impl FusedFuture + ?Sized)) -> bool { fut.is_terminated() }","tryCatchPattern":"// avoid double-poll entirely:\nlet mut fut = filter.or(fallback).fuse();\nmatch fut.poll_unpin(cx) {\n    Poll::Ready(v) => return v,\n    Poll::Pending => {} // do not poll again until woken\n}","preventionTips":["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."],"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"}