databendlabs/databend · error
Input plan must be Query, but it's
Error message
Input plan must be Query, but it's {} What it means
build_query in interpreter_copy_into_table (src/query/service/src/interpreters/interpreter_copy_into_table.rs:216) destructures the bound plan expecting Plan::Query to build a SelectInterpreter; any other variant hits unreachable!("Input plan must be Query, but it's {}"). COPY INTO <table> requires its source to be a query plan; a non-query plan here is an internal invariant violation.
Solutions
- Rewrite the COPY INTO source as an explicit SELECT: `COPY INTO target FROM (SELECT ... FROM @stage ...)`
- Upgrade to a version where the copy-into-table binder guarantees a Query plan or raises a clear user error
- If already using SELECT, file a bug with the exact SQL and Databend version
- As a code fix, replace unreachable! with a returned ErrorCode::Internal naming the unexpected variant
Example fix
// before
let (s_expr, metadata, bind_context, formatted_ast) = match plan {
Plan::Query { s_expr, metadata, bind_context, formatted_ast, .. } => (...),
v => unreachable!("Input plan must be Query, but it's {}", v),
};
// after
let (...) = match plan {
Plan::Query { .. } => (...),
v => return Err(ErrorCode::Internal(format!("Input plan must be Query, but it's {}", v))),
}; Defensive patterns
Strategy: validation
Validate before calling
-- ensure COPY INTO <table> source is an explicit SELECT -- good: COPY INTO t FROM (SELECT $1, $2 FROM @stage) -- avoid relying on implicit table/stage source binding
Type guard
if !matches!(plan, Plan::Query { .. }) {
return Err(ErrorCode::BadArguments("COPY INTO table source must be a SELECT".into()));
} Try / catch
match plan {
Plan::Query { s_expr, metadata, bind_context, formatted_ast, .. } => build_query(...),
other => return Err(ErrorCode::Internal(format!("Input plan must be Query, but it's {}", other))),
} Prevention
- Use explicit SELECT sources for COPY INTO table statements
- Verify COPY syntax against the docs for your Databend version after upgrades
- Run EXPLAIN on the source SELECT to confirm it binds as a query before COPY
When it happens
Trigger: Executing COPY INTO <table> FROM ... where the bound source plan is not Plan::Query — e.g. a stage/table source form the binder no longer wraps in a query plan, or a planner refactor emitting a different variant reached build_physical_plan -> build_query.
Common situations: COPY INTO a table from a stage with a source form that binds unexpectedly; statements copied from older Databend syntax after an upgrade changed binder output; internal regressions where Explain or DML wrapper plans leak into the copy path.
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
- logic error: expected CreateTable plan
- Input plan must be Query, but it's
- internal error: entered unreachable code
- table name is provided
- internal error: entered unreachable code
AI-assisted analysis of databendlabs/databend@288d84d76e (2026-09-11).
Data as JSON: /api/errors/6af69ef70cc8d4b3.
Report an issue: GitHub.
Appendix: source
Thrown at src/query/service/src/interpreters/interpreter_copy_into_table.rs:216
/// Create a CopyInterpreter with context and [`CopyIntoTablePlan`].
pub fn try_create(ctx: Arc<QueryContext>, plan: CopyIntoTablePlan) -> Result<Self> {
Ok(CopyIntoTableInterpreter { ctx, plan })
}
#[async_backtrace::framed]
async fn build_query(
&self,
query: &Plan,
) -> Result<(SelectInterpreter, Vec<UpdateStreamMetaReq>)> {
let (s_expr, metadata, bind_context, formatted_ast) = match query {
Plan::Query {
s_expr,
metadata,
bind_context,
formatted_ast,
..
} => (s_expr, metadata, bind_context, formatted_ast),
v => unreachable!("Input plan must be Query, but it's {}", v),
};
let update_stream_meta = dml_build_update_stream_req(self.ctx.clone()).await?;
let select_interpreter = SelectInterpreter::try_create(
self.ctx.clone(),
*(bind_context.clone()),
*s_expr.clone(),
metadata.clone(),
formatted_ast.clone(),
false,
)?;
Ok((select_interpreter, update_stream_meta))
}
#[async_backtrace::framed]
pub async fn build_physical_plan(View on GitHub (pinned to 288d84d76e)