dbt-labs/dbt-core · info

string prefix checked

Error message

string prefix checked

What it means

This is a panic from `expect("string prefix checked")` inside `is_time`. The function only enters the branch after `field.name().starts_with("time:")` passes, so `strip_prefix("time:")` is guaranteed by construction to succeed. It is an internal invariant assertion: if it ever fires, the code's own guard logic is broken, not the caller's input.

Source

Thrown at crates/dbt-adapter/src/sql_types.rs:1067

            }
        }

        pub fn unwrap(self) -> TimePrecision {
            match self {
                IsTimestamp::No => panic!("Cannot unwrap IsTimestamp::No"),
                IsTimestamp::Yes(precision) => precision,
            }
        }
    }

    pub fn is_time(data_type: &DataType) -> IsTimestamp {
        match data_type {
            DataType::FixedSizeList(field, 1) if field.name().starts_with("time:") => {
                IsTimestamp::Yes(TimePrecision::new(
                    field
                        .name()
                        .strip_prefix("time:")
                        .expect("string prefix checked")
                        .parse::<u8>()
                        .expect("invalid serialized time precision"),
                ))
            }
            _ => IsTimestamp::No,
        }
    }

    pub fn is_timestamp_ntz(data_type: &DataType) -> IsTimestamp {
        match data_type {
            DataType::FixedSizeList(field, 1) if field.name().starts_with("timestamp_ntz:") => {
                IsTimestamp::Yes(TimePrecision::new(
                    field
                        .name()
                        .strip_prefix("timestamp_ntz:")
                        .expect("string prefix checked")
                        .parse::<u8>()
                        .expect("invalid serialized timestamp precision"),

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Inspect the panic backtrace to see which of the four similar helpers (is_time/is_timestamp_ntz/ltz/tz) diverged between its starts_with guard and strip_prefix literal, and make them consistent.
  2. If you recently edited the match guard, restore the exact prefix literal in `strip_prefix` (e.g. "time:") so it matches the guard.
  3. As a hardening step, replace the starts_with/strip_prefix pair with `let Some(precision_str) = field.name().strip_prefix("time:") else { return IsTimestamp::No };` so the invariant is enforced by the type system instead of an expect.

Example fix

// before
DataType::FixedSizeList(field, 1) if field.name().starts_with("time:") => {
    IsTimestamp::Yes(TimePrecision::new(
        field.name().strip_prefix("time:").expect("string prefix checked")...
    ))
}
// after
DataType::FixedSizeList(field, 1) => {
    let Some(name) = field.name().strip_prefix("time:") else { return IsTimestamp::No };
    IsTimestamp::Yes(TimePrecision::new(name.parse::<u8>().unwrap_or_else(
        |_| panic!("invalid serialized time precision: {}", field.name()))))
}
Defensive patterns

Strategy: validation

Validate before calling

fn is_safe_time_field(data_type: &DataType) -> bool {
    matches!(data_type, DataType::FixedSizeList(f, 1))
        && matches!(data_type, DataType::FixedSizeList(f, _) if f.name().starts_with("time:"))
        && f.name().strip_prefix("time:").map(|s| s.parse::<u8>().is_ok()).unwrap_or(false)
}

Type guard

fn time_precision_field(dt: &DataType) -> Option<u8> {
    if let DataType::FixedSizeList(f, 1) = dt {
        f.name().strip_prefix("time:").and_then(|s| s.parse::<u8>().ok())
    } else { None }
}

Prevention

When it happens

Trigger: Effectively unreachable. It can only panic if `is_time` is called with a `DataType::FixedSizeList` whose field name starts with "time:" yet the immediately following `strip_prefix("time:")` fails — impossible under current std semantics; would require the guard and the strip to diverge (e.g. a code edit changing one but not the other).

Common situations: Developers essentially never hit this at runtime. It surfaces only during refactors — e.g. changing the `starts_with` guard to a different check (case-insensitive match, trimmed name) without updating the `strip_prefix` literal, or replacing `expect` with `unwrap` after editing the match arm.

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 dbt-labs/dbt-core@0267ce9170 (2026-09-07). Data as JSON: /api/errors/e67af333316af312. Report an issue: GitHub.