{"record":{"id":"b832762d86121bdd","repo":"databendlabs/databend","slug":"plan-in-insertinputsource-stag-must-be-copyintota-b83276","errorCode":null,"errorMessage":"plan in InsertInputSource::Stag must be CopyIntoTable","messagePattern":"plan in InsertInputSource::Stag must be CopyIntoTable","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/query/sql/src/planner/plans/insert.rs","lineNumber":269,"sourceCode":"                    FormatTreeNode::new(format!(\"from_stage_attachment: {from_stage_attachment}\")),\n                    FormatTreeNode::new(format!(\n                        \"required_values_schema: [{required_values_schema}]\"\n                    )),\n                    FormatTreeNode::new(format!(\n                        \"required_source_schema: [{required_source_schema}]\"\n                    )),\n                    FormatTreeNode::new(format!(\"write_mode: {write_mode}\")),\n                    FormatTreeNode::new(format!(\"validation_mode: {validation_mode}\")),\n                    FormatTreeNode::new(format!(\"stage_table_info: {stage_table_info}\")),\n                    FormatTreeNode::new(format!(\"enable_distributed: {enable_distributed}\")),\n                ];\n                children.extend(stage_node);\n                Ok(\n                    FormatTreeNode::with_children(format!(\"{plan_name} (stage):\"), children)\n                        .format_pretty()?,\n                )\n            }\n            _ => unreachable!(\"plan in InsertInputSource::Stag must be CopyIntoTable\"),\n        },\n    }\n}\n\nimpl std::fmt::Debug for Insert {\n    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {\n        f.debug_struct(\"Insert\")\n            .field(\"catalog\", &self.catalog)\n            .field(\"database\", &self.database)\n            .field(\"table\", &self.table)\n            .field(\"branch\", &self.branch)\n            .field(\"schema\", &self.schema)\n            .field(\"overwrite\", &self.overwrite)\n            .finish()\n    }\n}\n","sourceCodeStart":251,"sourceCodeEnd":286,"githubUrl":"https://github.com/databendlabs/databend/blob/288d84d76e20a2f8f7173bda9691eb6ece301aa9/src/query/sql/src/planner/plans/insert.rs#L251-L286","documentation":"During EXPLAIN of an INSERT, format_insert_source expects the plan stored in InsertInputSource::Stag to be a CopyIntoTable plan (it renders the stage child tree); any other plan shape hits `unreachable!(\"plan in InsertInputSource::Stag must be CopyIntoTable\")`. The binder is the only producer of this variant, so the panic indicates the binder attached a non-copy-into plan to a stage input.","triggerScenarios":"Running EXPLAIN (or EXPLAIN VERBOSE) on an INSERT ... INTO ... FROM @stage statement whose bound stage input plan is not CopyIntoTable — e.g. binder changes produce a different plan wrapper for stage sources.","commonSituations":"Hit by users explaining data-loading statements (COPY-style INSERT from a named stage or files) after binder/planner refactors; shows up only on the EXPLAIN path, not execution.","solutions":["Inspect the binder code that constructs InsertInputSource::Stag and confirm it always wraps a CopyIntoTable plan.","Add the actual plan debug output to the unreachable! message for faster diagnosis.","If other plan shapes are legitimate, match them explicitly and format accordingly instead of panicking.","Check recent changes to insert/stage binding; use git blame on the producer of the variant."],"exampleFix":"// before\n_ => unreachable!(\"plan in InsertInputSource::Stag must be CopyIntoTable\"),\n// after\nother => FormatTreeNode::with_children(format!(\"{plan_name} ({other:?}):\"), vec![]).format_pretty(),","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// Skip EXPLAIN of stage inserts if the version is affected; execute directly\nmatch try_explain(stmt) {\n    Err(e) if e.contains(\"Stag must be CopyIntoTable\") => exec_insert(stmt),\n    r => r,\n}","preventionTips":["Only EXPLAIN the INSERT ... FROM @stage statement on versions where the binder is verified.","Use COPY INTO instead of INSERT-from-stage for stage loading if the EXPLAIN path panics.","Keep stage-file format options simple to avoid alternate binder paths.","Report the statement text with the panic to pinpoint the binder change."],"tags":["rust","planner","insert","explain","panic"],"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"}