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_location (src/query/service/src/interpreters/interpreter_copy_into_location.rs:64) destructures the bound plan expecting Plan::Query and panics with unreachable!("Input plan must be Query, but it's {}") for any other variant. COPY INTO <location> requires its input to be a SELECT/query plan; a non-query plan reaching this interpreter is an internal invariant breach.

Solutions

  1. Rewrite the COPY INTO statement so the source is an explicit SELECT, e.g. `COPY INTO @my_stage FROM (SELECT col1, col2 FROM my_table ...)`, since plain table sources may no longer bind as expected
  2. Upgrade to a Databend version where the copy-into-location binder guarantees a Query plan or returns a proper user-facing error
  3. If the statement already uses SELECT, file a bug with the exact SQL and version — the binder produced an unexpected plan variant
  4. As a code fix, return ErrorCode::Internal with the variant name instead of panicking

Example fix

-- before (triggers panic on some builds)
COPY INTO @stage FROM my_table FILE_FORMAT = (TYPE = CSV);
-- after
COPY INTO @stage FROM (SELECT * FROM my_table) FILE_FORMAT = (TYPE = CSV);
Defensive patterns

Strategy: validation

Validate before calling

-- ensure COPY INTO <location> source is an explicit SELECT
-- good: COPY INTO @stage FROM (SELECT ...)
-- avoid: COPY INTO @stage FROM <table> on builds that bind it non-query

Type guard

if !matches!(plan, Plan::Query { .. }) {
    return Err(ErrorCode::BadArguments("COPY INTO location source must be a SELECT".into()));
}

Try / catch

match plan {
    Plan::Query { s_expr, metadata, bind_context, formatted_ast, .. } => build(...),
    other => Err(ErrorCode::Internal(format!("Input plan must be Query, but it's {}", other))),
}

Prevention

When it happens

Trigger: Executing COPY INTO <stage/location> whose source binds to a non-Query plan — e.g. `COPY INTO @stage FROM (a non-select statement)` or a planner change that produces a different plan variant (Explain/DML wrappers) for the copy source.

Common situations: Users attempting COPY INTO a location from something other than a SELECT (a table name shortcut or unsupported source form) on a build where the binder no longer wraps it into a query plan; also seen after version upgrades that changed plan binding.

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


AI-assisted analysis of databendlabs/databend@288d84d76e (2026-09-11). Data as JSON: /api/errors/595431d9d84d18a2. Report an issue: GitHub.

Appendix: source

Thrown at src/query/service/src/interpreters/interpreter_copy_into_location.rs:64

    /// Create a CopyInterpreter with context and [`CopyIntoLocationPlan`].
    pub fn try_create(ctx: Arc<QueryContext>, plan: CopyIntoLocationPlan) -> Result<Self> {
        Ok(CopyIntoLocationInterpreter { 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 select_interpreter = SelectInterpreter::try_create(
            self.ctx.clone(),
            *(bind_context.clone()),
            *s_expr.clone(),
            metadata.clone(),
            formatted_ast.clone(),
            false,
        )?;

        let update_stream_meta = dml_build_update_stream_req(self.ctx.clone()).await?;

        Ok((select_interpreter, update_stream_meta))
    }

    /// Build a pipeline for local copy into stage.
    #[async_backtrace::framed]

View on GitHub (pinned to 288d84d76e)