{"record":{"id":"13dcae15a48a2864","repo":"quickwit-oss/quickwit","slug":"request-should-be-set","errorCode":null,"errorMessage":"request should be set","messagePattern":"request should be set","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"quickwit/quickwit-common/src/tower/event_listener.rs","lineNumber":103,"sourceCode":"    inner: F,\n    event_broker: EventBroker,\n    request: Option<R>,\n}\n\nimpl<R, F, T, E> Future for ResponseFuture<F, R>\nwhere\n    R: Event,\n    F: Future<Output = Result<T, E>>,\n{\n    type Output = Result<T, E>;\n\n    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {\n        let this = self.project();\n        let response = ready!(this.inner.poll(cx));\n\n        if response.is_ok() {\n            this.event_broker\n                .publish(this.request.take().expect(\"request should be set\"));\n        }\n        Poll::Ready(Ok(response?))\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use std::sync::Arc;\n    use std::sync::atomic::{AtomicUsize, Ordering};\n    use std::time::Duration;\n\n    use async_trait::async_trait;\n\n    use super::*;\n    use crate::pubsub::EventSubscriber;\n\n    #[derive(Debug, Clone, Copy)]\n    struct MyEvent {","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/quickwit-oss/quickwit/blob/a39730c5cdcd1a4fe798403737ae293999ea21f8/quickwit/quickwit-common/src/tower/event_listener.rs#L85-L121","documentation":"This `.expect(\"request should be set\")` panic fires in `EventListener::poll` (quickwit-common/src/tower/event_listener.rs). The tower `Service` contract requires `poll_ready` to be called (and return Ready) before each `call`; `call` stores the request so that after the inner future resolves successfully, an event can be published for it. The panic means the future was polled after the request was already taken, or `call` was never invoked properly through the service stack — a violation of the tower protocol by the caller or a middleware above.","triggerScenarios":"Polling the response future returned by `EventListener::call` more times than allowed after completion, calling `call` without a prior successful `poll_ready` such that the request slot handling is desynchronized, or a custom wrapper that clones/caches the future and polls it twice.","commonSituations":"Hand-written tower middleware or load-balancing layers that violate the poll_ready/call contract; code that collects service futures and re-polls them; custom service implementations built on quickwit's event listener layer that bypass `ServiceExt::ready()`.","solutions":["Ensure every request goes through the tower protocol: call `poll_ready` (or `.ready().await`) and wait for `Poll::Ready(Ok(()))` before each `call`.","Use `tower::ServiceExt::oneshot(service, request)` or `service.ready().await?.call(req)` instead of calling `call` directly.","Do not poll the returned future after it has resolved; drop it after completion.","If a custom layer wraps this service, verify it does not clone or double-poll inner futures and forwards `poll_ready` correctly."],"exampleFix":"// before: calling without readiness\nlet fut = service.call(request);\nlet res = fut.await;\n\n// after: follow the tower protocol\nuse tower::ServiceExt;\nlet response = service.ready().await?.call(request).await?;","handlingStrategy":"type-guard","validationCode":"// Always route requests through readiness-aware helpers\nuse tower::ServiceExt;\n// let response = event_listener_service.ready().await?.call(req).await?;","typeGuard":"// Only issue a call when the service is ready\nfn is_ready<S: tower::Service<R>, R>(svc: &mut S, cx: &mut std::task::Context<'_>) -> bool {\n    use tower::Service;\n    matches!(svc.poll_ready(cx), std::task::Poll::Ready(Ok(())))\n}","tryCatchPattern":"// Panics here indicate a protocol bug; catch at the server boundary in tests\nlet result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {\n    // drive the service via oneshot\n    tower::ServiceExt::oneshot(&mut svc, req)\n}));","preventionTips":["Never call `call` directly; use ServiceExt::ready/oneshot.","Never poll a response future after it has completed.","When writing middleware, always forward poll_ready and respect its result.","Avoid caching/cloning inner futures across polls."],"tags":["rust","tower","middleware","panic","async"],"backgroundTag":"internal-invariant-violation","analyzedSha":"a39730c5cdcd1a4fe798403737ae293999ea21f8","analyzedAt":"2026-09-08T13:19:37.784Z","contentChangedAt":"2026-09-08T13:19:37.784Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}