{"record":{"id":"a0fe846e87e64b8f","repo":"databendlabs/databend","slug":"unreachable-a0fe84","errorCode":null,"errorMessage":"_ => unreachable!()","messagePattern":"_ => unreachable!\\(\\)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/query/service/src/pipelines/processors/transforms/hash_join/transform_hash_join_probe.rs","lineNumber":382,"sourceCode":"                            self.fill_rows(task)?;\n                            return Ok(());\n                        }\n                    }\n                    FinalScanType::MergeInto => {\n                        if let Some(item) = self\n                            .join_probe_state\n                            .final_merge_into_partial_unmodified_scan_task()\n                        {\n                            self.final_merge_into_partial_unmodified_scan(item)?;\n                            return Ok(());\n                        }\n                    }\n                }\n                // No more task, final scan finished.\n                self.is_final_scan_finished = true;\n                Ok(())\n            }\n            _ => unreachable!(),\n        }\n    }\n\n    #[async_backtrace::framed]\n    async fn async_process(&mut self) -> Result<()> {\n        match self.step {\n            Step::Async(AsyncStep::WaitBuild) => {\n                self.hash_table_type = self\n                    .join_probe_state\n                    .hash_join_state\n                    .wait_build_notify()\n                    .await?;\n                self.is_spill_happened = self\n                    .join_probe_state\n                    .hash_join_state\n                    .is_spill_happened\n                    .load(Ordering::Acquire);\n                self.is_build_finished = true;","sourceCodeStart":364,"sourceCodeEnd":400,"githubUrl":"https://github.com/databendlabs/databend/blob/288d84d76e20a2f8f7173bda9691eb6ece301aa9/src/query/service/src/pipelines/processors/transforms/hash_join/transform_hash_join_probe.rs#L364-L400","documentation":"The probe transform's process() matches on its Step state machine; the catch-all panics because a step invalid for the synchronous probe phase reached process(). Like the build transform's equivalents, this asserts pipeline scheduling never routes unhandled steps here.","triggerScenarios":"The step field contains a variant not handled by process() (e.g. an async-only step such as WaitBuild, or a finish step routed incorrectly) due to a transition or scheduling bug.","commonSituations":"After adding new Step/AsyncStep variants; races between probe final scan and build wait; regressions from refactoring the probe transform's event loop.","solutions":["Add or correct the match arm for the offending step in process()","Embed the step value in the panic message to ease debugging","Audit the probe transform's state transitions so each step is consumed in exactly one phase","Reproduce with concurrent probe/final-scan workloads and check scheduler logs"],"exampleFix":"// before\n_ => unreachable!(),\n// after\nother => unreachable!(\"TransformHashJoinProbe::process got unexpected step: {:?}\", other),","handlingStrategy":"validation","validationCode":"// Verify the step belongs to the synchronous probe phase\nif !matches!(self.step, Step::Consume | Step::Sync(_)) {\n    return Err(ErrorCode::Internal(\"probe process() called with invalid step\"));\n}","typeGuard":"fn is_probe_sync_step(s: &Step) -> bool {\n    !matches!(s, Step::Async(_))\n}","tryCatchPattern":null,"preventionTips":["Keep Step handling exhaustive and centralized per transform","Add the step payload to panic messages for diagnosability","Cover probe + final-scan + build-wait interleavings with pipeline tests"],"tags":["rust","panic","state-machine","hash-join","pipeline"],"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"}