databendlabs/databend · critical
Query info is None
Error message
Query info is None
What it means
`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.
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.
Example fix
// before
let info = self.info.as_mut().expect("Query info is None");
// after
let info = self.info.as_mut().ok_or_else(|| {
ErrorCode::Internal("Query info is None when executing pipeline")
})?; Defensive patterns
Strategy: try-catch
Type guard
if manager.info.is_none() { return Err(ErrorCode::Internal("query not initialized")); } Try / catch
// Wrap pipeline execution in catch_unwind at the executor-thread boundary
std::panic::catch_unwind(AssertUnwindSafe(|| manager.execute_pipeline()))
.unwrap_or_else(|_| log::error!("execute_pipeline panicked: QueryInfo missing")); Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
AI-assisted analysis of databendlabs/databend@288d84d76e (2026-09-11).
Data as JSON: /api/errors/64aede4e2cf58285.
Report an issue: GitHub.
Appendix: source
Thrown at src/query/service/src/servers/flight/v1/exchange/exchange_manager.rs:1276
pub fn shutdown_query(&mut self, cause: Option<ErrorCode>) {
if let Some(query_info) = &mut self.info {
if let Some(query_executor) = &query_info.query_executor {
query_executor.finish(cause);
}
if let Some(worker) = query_info.remove_leak_query_worker.take() {
worker.abort();
}
}
}
pub fn on_finished(self) {
// Do something when query finished.
}
pub fn execute_pipeline(&mut self) -> Result<()> {
let info = self.info.as_mut().expect("Query info is None");
let perf_guard = {
let pc = info.query_ctx.get_perf_config();
if pc.profiler_enabled && !self.is_request_server {
Some(QueryPerf::start(pc.frequency)?)
} else {
None
}
};
if !info.started.swap(true, Ordering::SeqCst) {
if let Some(leak_worker) = info.remove_leak_query_worker.take() {
leak_worker.abort();
}
}
if self.fragments_coordinator.is_empty() {
// Empty fragments if it is a request server, because the pipelines may have been linked.View on GitHub (pinned to 288d84d76e)