databendlabs/databend · error
ExecutorTask::None => unreachable!()
Error message
ExecutorTask::None => unreachable!()
What it means
This is a Rust `unreachable!()` panic in `QueriesExecutorTasks::push_task`. The `ExecutorTask` enum has a `None` variant used as a placeholder/empty state, and the executor's internal invariant is that only real tasks (Sync, Async, AsyncCompleted) are ever pushed onto a worker's queue. Pushing `ExecutorTask::None` indicates an executor bookkeeping bug.
Solutions
- Inspect the caller of `push_task` to find where an `ExecutorTask::None` was produced instead of a real task
- Guard call sites with `if !matches!(task, ExecutorTask::None)` before pushing, or return a proper error instead of panicking
- Reproduce with the failing query and enable executor debug logs (trace!) to trace which scheduling step emitted the None task
- Check for recent regressions in the queries executor refactor in src/query/service/src/pipelines/executor/
Example fix
// before
match task {
ExecutorTask::None => unreachable!(),
ExecutorTask::Sync(p) => sync_queue.push_back(p),
...
}
// after
match task {
ExecutorTask::None => return Err(ErrorCode::Internal("push_task called with ExecutorTask::None")),
ExecutorTask::Sync(p) => sync_queue.push_back(p),
...
} Defensive patterns
Strategy: type-guard
Validate before calling
if matches!(task, ExecutorTask::None) { return Err(ErrorCode::Internal("task is ExecutorTask::None")); } Type guard
fn is_real_task(t: &ExecutorTask) -> bool { !matches!(t, ExecutorTask::None) } Prevention
- Never push default/placeholder ExecutorTask values; initialize worker slots with real tasks or Option
- Add debug_assert!(!matches!(task, ExecutorTask::None)) at all push_task call sites
- Cover executor scheduling paths with tests that enumerate all ExecutorTask variants
When it happens
Trigger: Calling `push_task` with an `ExecutorTask::None` value, which should only happen if the executor's scheduling logic built or forwarded a placeholder task instead of an actual processor/completion notification.
Common situations: Custom pipeline/executor modifications in the Databend query engine, or a bug in task scheduling where a slot initialized as `None` is pushed without being replaced by a real task.
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
- ExecutorTask::None => unreachable!()
- Logic error: cannot change priority for…
- used for new executor
- {}
- Temp table id used up
AI-assisted analysis of databendlabs/databend@288d84d76e (2026-09-11).
Data as JSON: /api/errors/a371df31e451a3ee.
Report an issue: GitHub.
Appendix: source
Thrown at src/query/service/src/pipelines/executor/queries_executor_tasks.rs:364
}
}
ExecutorTask::None
}
pub fn push_task(&mut self, worker_id: usize, task: ExecutorTask) {
self.tasks_size += 1;
debug_assert!(
worker_id < self.workers_sync_tasks.len(),
"out of index, {}, {}",
worker_id,
self.workers_sync_tasks.len()
);
let sync_queue = &mut self.workers_sync_tasks[worker_id];
let completed_queue = &mut self.workers_completed_async_tasks[worker_id];
let async_queue = &mut self.workers_async_tasks[worker_id];
match task {
ExecutorTask::None => unreachable!(),
ExecutorTask::Sync(processor) => sync_queue.push_back(processor),
ExecutorTask::Async(processor) => async_queue.push_back(processor),
ExecutorTask::AsyncCompleted(task) => completed_queue.push_back(task),
}
}
}
View on GitHub (pinned to 288d84d76e)