databendlabs/databend · error
internal error: entered unreachable code
Error message
internal error: entered unreachable code
What it means
This is a Rust `unreachable!()` panic in `build_pipeline2` of `PhysicalMutation`. The code asserts that `MutationStrategy::Direct` never reaches this match arm when computing `serialize_len` (the output port count after optionally removing the row-id port). Hitting it means the mutation plan was constructed with a `Direct` strategy but the pipeline-build path still ran this branch — an internal planner invariant violation, not user input error.
Solutions
- Capture the failing query's EXPLAIN/plan output and file a Databend bug with the MERGE INTO statement and plan, since this is an internal invariant violation
- Check the Databend version and upgrade to the latest release where Direct-strategy handling may have been fixed
- Inspect how the mutation plan was created (which code path chose MutationStrategy::Direct) and ensure Direct plans take a different pipeline-build path
- As a workaround, rewrite the MERGE INTO so the optimizer classifies it as MatchedOnly/MixedMatched/NotMatchedOnly instead of Direct
Example fix
// before MutationStrategy::Direct => unreachable!(), // after MutationStrategy::Direct => 0, // Direct strategy does not serialize matched/not-matched rows
Defensive patterns
Strategy: try-catch
Validate before calling
// Rust: callers cannot inspect strategy directly; guard at API boundary
if matches!(plan.strategy(), MutationStrategy::Direct) {
return Err(ErrorCode::Internal("Direct strategy not supported by this build path"));
} Type guard
fn is_direct(s: &MutationStrategy) -> bool { matches!(s, MutationStrategy::Direct) } Try / catch
// Catch the panic at the query-execution boundary
let result = std::panic::catch_unwind(AssertUnwindSafe(|| builder.build_pipeline2(...)));
match result { Ok(r) => r, Err(_) => Err(ErrorCode::Internal("mutation pipeline build panicked")) } Prevention
- Never construct PhysicalMutation with Direct strategy through the generic build path
- Add a debug_assert in plan construction that Direct plans use the dedicated fast path
- Cover all MutationStrategy variants in pipeline-build unit tests
- Keep planner strategy classification and pipeline builders updated together in the same PR
When it happens
Trigger: Executing a MERGE INTO physical mutation plan whose `strategy` is `MutationStrategy::Direct` while `build_pipeline2` executes the `serialize_len` computation; i.e., a Direct-strategy plan was routed through a build path that expects NotMatchedOnly/MixedMatched/MatchedOnly.
Common situations: Running Databend MERGE INTO queries after planner changes that allow Direct strategy to reach this builder; custom or patched plans mixing strategy kinds; version upgrades where strategy classification changed but pipeline construction did not.
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
- internal error: entered unreachable code
- internal error: entered unreachable code
- internal error: entered unreachable code
- internal error: entered unreachable code
- unexpected value type for grouping function
AI-assisted analysis of databendlabs/databend@288d84d76e (2026-09-11).
Data as JSON: /api/errors/70d30b1beac6ad58.
Report an issue: GitHub.
Appendix: source
Thrown at src/query/service/src/physical_plans/physical_mutation.rs:186
let table = FuseTable::try_from_table(tbl.as_ref())?;
let block_thresholds = table.get_block_thresholds();
let input_schema = DataSchema::from(table.schema_with_stream()).into();
let cluster_stats_gen =
table.get_cluster_stats_gen(builder.ctx.clone(), 0, block_thresholds, input_schema)?;
let max_threads = builder.settings.get_max_threads()? as usize;
// For row_id port, create rowid_aggregate_mutator
// For matched data port and unmatched port, do serialize
let serialize_len = match self.strategy {
MutationStrategy::NotMatchedOnly => builder.main_pipeline.output_len(),
MutationStrategy::MixedMatched | MutationStrategy::MatchedOnly => {
// remove row id port
builder.main_pipeline.output_len() - 1
}
MutationStrategy::Direct => unreachable!(),
};
// 1. Fill default and computed columns
builder.build_fill_columns_in_merge_into(
tbl.clone(),
serialize_len,
self.need_match,
self.unmatched.clone(),
)?;
// 2. Add cluster‘s blocksort if it's a cluster table
builder.build_compact_and_cluster_sort_in_merge_into(
table,
self.need_match,
serialize_len,
block_thresholds,
)?;
View on GitHub (pinned to 288d84d76e)