{"record":{"id":"64aede4e2cf58285","repo":"databendlabs/databend","slug":"query-info-is-none","errorCode":null,"errorMessage":"Query info is None","messagePattern":"Query info is None","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/query/service/src/servers/flight/v1/exchange/exchange_manager.rs","lineNumber":1276,"sourceCode":"\n    pub fn shutdown_query(&mut self, cause: Option<ErrorCode>) {\n        if let Some(query_info) = &mut self.info {\n            if let Some(query_executor) = &query_info.query_executor {\n                query_executor.finish(cause);\n            }\n\n            if let Some(worker) = query_info.remove_leak_query_worker.take() {\n                worker.abort();\n            }\n        }\n    }\n\n    pub fn on_finished(self) {\n        // Do something when query finished.\n    }\n\n    pub fn execute_pipeline(&mut self) -> Result<()> {\n        let info = self.info.as_mut().expect(\"Query info is None\");\n\n        let perf_guard = {\n            let pc = info.query_ctx.get_perf_config();\n            if pc.profiler_enabled && !self.is_request_server {\n                Some(QueryPerf::start(pc.frequency)?)\n            } else {\n                None\n            }\n        };\n\n        if !info.started.swap(true, Ordering::SeqCst) {\n            if let Some(leak_worker) = info.remove_leak_query_worker.take() {\n                leak_worker.abort();\n            }\n        }\n\n        if self.fragments_coordinator.is_empty() {\n            // Empty fragments if it is a request server, because the pipelines may have been linked.","sourceCodeStart":1258,"sourceCodeEnd":1294,"githubUrl":"https://github.com/databendlabs/databend/blob/288d84d76e20a2f8f7173bda9691eb6ece301aa9/src/query/service/src/servers/flight/v1/exchange/exchange_manager.rs#L1258-L1294","documentation":"`execute_pipeline` starts by taking `&mut` access to the stored `QueryInfo` via `self.info.as_mut().expect(\"Query info is None\")`. `QueryInfo` holds the query context, query id, and executor slot, so executing a pipeline without it is impossible; the engine treats a missing info as a fatal state-machine bug and panics. This typically means `execute_pipeline` was invoked on a manager that was never initialized or whose lifecycle already ended.","triggerScenarios":"Calling `execute_pipeline(&mut self)` when `self.info` is `None`: executing after `on_finished`/teardown consumed the manager state, or running a query whose init path failed silently before info was stored.","commonSituations":"Query cancelled or timed out concurrently with pipeline execution start; a driver/handler race where the query finish path runs before execution; custom integrations calling `execute_pipeline` without prior initialization in forks or dev builds.","solutions":["Inspect logs to confirm the query lifecycle order; the finish/teardown path should not run before `execute_pipeline` completes.","Fix the caller so `execute_pipeline` is only invoked between initialization and `on_finished` (e.g., gate on a state flag).","Convert the expect into a `Result` and propagate `ErrorCode::Internal` so the query fails gracefully instead of crashing the executor thread.","Check for concurrent mutation of the manager; `info` being already-taken often indicates a double-execution or use-after-finish race."],"exampleFix":"// before\nlet info = self.info.as_mut().expect(\"Query info is None\");\n\n// after\nlet info = self.info.as_mut().ok_or_else(|| {\n    ErrorCode::Internal(\"Query info is None when executing pipeline\")\n})?;","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"if manager.info.is_none() { return Err(ErrorCode::Internal(\"query not initialized\")); }","tryCatchPattern":"// Wrap pipeline execution in catch_unwind at the executor-thread boundary\nstd::panic::catch_unwind(AssertUnwindSafe(|| manager.execute_pipeline()))\n    .unwrap_or_else(|_| log::error!(\"execute_pipeline panicked: QueryInfo missing\"));","preventionTips":["Ensure cancel/timeout/finish paths are sequenced after pipeline execution (use a lifecycle state machine).","Call execute_pipeline exactly once per query and guard against re-entry.","Add debug assertions/logs when `info` transitions to None."],"tags":["distributed-query","panic","internal-state","rust"],"backgroundTag":"internal-invariant-violation","analyzedSha":"288d84d76e20a2f8f7173bda9691eb6ece301aa9","analyzedAt":"2026-09-11T11:29:36.208Z","contentChangedAt":"2026-09-11T11:29:36.208Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}