{"record":{"id":"70d30b1beac6ad58","repo":"databendlabs/databend","slug":"internal-error-entered-unreachable-code-70d30b","errorCode":null,"errorMessage":"internal error: entered unreachable code","messagePattern":"internal error: entered unreachable code","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/query/service/src/physical_plans/physical_mutation.rs","lineNumber":186,"sourceCode":"\n        let table = FuseTable::try_from_table(tbl.as_ref())?;\n        let block_thresholds = table.get_block_thresholds();\n\n        let input_schema = DataSchema::from(table.schema_with_stream()).into();\n        let cluster_stats_gen =\n            table.get_cluster_stats_gen(builder.ctx.clone(), 0, block_thresholds, input_schema)?;\n\n        let max_threads = builder.settings.get_max_threads()? as usize;\n\n        // For row_id port, create rowid_aggregate_mutator\n        // For matched data port and unmatched port, do serialize\n        let serialize_len = match self.strategy {\n            MutationStrategy::NotMatchedOnly => builder.main_pipeline.output_len(),\n            MutationStrategy::MixedMatched | MutationStrategy::MatchedOnly => {\n                // remove row id port\n                builder.main_pipeline.output_len() - 1\n            }\n            MutationStrategy::Direct => unreachable!(),\n        };\n\n        // 1. Fill default and computed columns\n        builder.build_fill_columns_in_merge_into(\n            tbl.clone(),\n            serialize_len,\n            self.need_match,\n            self.unmatched.clone(),\n        )?;\n\n        // 2. Add cluster‘s blocksort if it's a cluster table\n        builder.build_compact_and_cluster_sort_in_merge_into(\n            table,\n            self.need_match,\n            serialize_len,\n            block_thresholds,\n        )?;\n","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/databendlabs/databend/blob/288d84d76e20a2f8f7173bda9691eb6ece301aa9/src/query/service/src/physical_plans/physical_mutation.rs#L168-L204","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nMutationStrategy::Direct => unreachable!(),\n// after\nMutationStrategy::Direct => 0, // Direct strategy does not serialize matched/not-matched rows","handlingStrategy":"try-catch","validationCode":"// Rust: callers cannot inspect strategy directly; guard at API boundary\nif matches!(plan.strategy(), MutationStrategy::Direct) {\n    return Err(ErrorCode::Internal(\"Direct strategy not supported by this build path\"));\n}","typeGuard":"fn is_direct(s: &MutationStrategy) -> bool { matches!(s, MutationStrategy::Direct) }","tryCatchPattern":"// Catch the panic at the query-execution boundary\nlet result = std::panic::catch_unwind(AssertUnwindSafe(|| builder.build_pipeline2(...)));\nmatch result { Ok(r) => r, Err(_) => Err(ErrorCode::Internal(\"mutation pipeline build panicked\")) }","preventionTips":["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"],"tags":["rust","panic","unreachable","internal-invariant","merge-into","physical-plan"],"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"}