databendlabs/databend · error

internal error: entered unreachable code

Error message

internal error: entered unreachable code

What it means

load_can_auto_cast_to decides whether a stage (external-location) file column type can auto-cast to a target type during COPY/inferring. It panics with unreachable! when the target (to_type) is one of Null, EmptyArray, EmptyMap, Generic(_), or StageLocation, since no real table column should have those types. This is an internal invariant guard: some caller passed a type that should have been rejected earlier in planning.

Solutions

  1. Inspect the target schema/column types passed to the stage reader and ensure they are concrete, non-generic types
  2. Check where project_columnar builds its projection types; fix upstream type resolution so Null/EmptyArray/EmptyMap/Generic/StageLocation never become to_type
  3. If a legitimate new case exists, add a match branch instead of relying on unreachable
  4. Report the panic with the query and schema to Databend maintainers as a bug

Example fix

// before
let target = DataType::Generic(0); // unresolved type in projection
assert!(load_can_auto_cast_to(&from_ty, &target));
// after
let target = resolve_generic(&target, &schema)?; // resolve to a concrete DataType first
assert!(load_can_auto_cast_to(&from_ty, &target));
Defensive patterns

Strategy: validation

Validate before calling

// rust: before invoking stage auto-cast logic
fn is_valid_target(ty: &DataType) -> bool {
    !matches!(ty, DataType::Null | DataType::EmptyArray | DataType::EmptyMap
        | DataType::Generic(_) | DataType::StageLocation)
}
assert!(is_valid_target(&target_type), "target type cannot be auto-cast target");

Type guard

fn is_concrete_type(ty: &DataType) -> bool {
    !matches!(ty, DataType::Null | DataType::EmptyArray | DataType::EmptyMap
        | DataType::Generic(_) | DataType::StageLocation)
}

Prevention

When it happens

Trigger: Calling load_can_auto_cast_to (directly or via project_columnar) with a to_type of DataType::Null, EmptyArray, EmptyMap, Generic(n), or StageLocation while reading a stage file.

Common situations: Table schemas or planned projections containing unresolved/generic or empty-collection types reaching stage read; bugs in schema inference for stage files; hand-built queries targeting synthetic types.

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/4a9a0b8a8ca519fa. Report an issue: GitHub.

Appendix: source

Thrown at src/query/storages/common/stage/src/read/cast.rs:59

///
/// ## Permitting Casts:
///
///  - Specificity: Encourages loading into a more specific type, as it typically requires less storage or provides more information. which is valuable in ETL processes.
///     -  Often accompanied by safety measures: Requires a specific format, and any mismatch will readily trigger an error, minimizing significant issues in production.
///  - Convenience: For instance, Python users might use the Python int type for convenience, which corresponds to int64 in Parquet. Thus, casting from int64 to smaller integers is allowed.
///  - Compatibility: Initially, the rules are based on the intersection of arrow_cast::can_cast_to() and all pairs that are operational from running run_cast, both of which are quite permissive.
///     - but maybe not a big issue, since user can start with infer_schema.
pub fn load_can_auto_cast_to(from_type: &DataType, to_type: &DataType) -> bool {
    use DataType::*;
    use NumberDataType::*;
    // note this does not cover diff Number(_) | Decimal(_)
    if from_type == to_type {
        return true;
    }
    // we mainly care about which types can/cannot cast to to_type.
    // the match branches is grouped in a way to make it easier to read this info.
    match (from_type, to_type) {
        (_, Null | EmptyArray | EmptyMap | Generic(_) | StageLocation) => unreachable!(),

        // ====  remove null first, all trivial
        (Null, Nullable(_)) => true,
        (Nullable(box from_ty), Nullable(box to_ty))
        | (from_ty, Nullable(box to_ty))
        | (Nullable(box from_ty), to_ty) => load_can_auto_cast_to(from_ty, to_ty),

        // ==== dive into nested types, must from the same out type, all trivial
        (Map(box from_ty), Map(box to_ty)) => match (from_ty, to_ty) {
            (Tuple(_), Tuple(_)) => load_can_auto_cast_to(from_ty, to_ty),
            (_, _) => unreachable!(),
        },
        (EmptyMap, Map(_)) => true,
        (_, Map(_)) | (Map(_), _) => false,

        (Tuple(from_tys), Tuple(to_tys)) => {
            from_tys.len() == to_tys.len()
                && from_tys

View on GitHub (pinned to 288d84d76e)