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

  1. Inspect logs to confirm the query lifecycle order; the finish/teardown path should not run before `execute_pipeline` completes.
  2. Fix the caller so `execute_pipeline` is only invoked between initialization and `on_finished` (e.g., gate on a state flag).
  3. Convert the expect into a `Result` and propagate `ErrorCode::Internal` so the query fails gracefully instead of crashing the executor thread.
  4. 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

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)