{"record":{"id":"e67af333316af312","repo":"dbt-labs/dbt-core","slug":"string-prefix-checked","errorCode":null,"errorMessage":"string prefix checked","messagePattern":"string prefix checked","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"info","filePath":"crates/dbt-adapter/src/sql_types.rs","lineNumber":1067,"sourceCode":"            }\n        }\n\n        pub fn unwrap(self) -> TimePrecision {\n            match self {\n                IsTimestamp::No => panic!(\"Cannot unwrap IsTimestamp::No\"),\n                IsTimestamp::Yes(precision) => precision,\n            }\n        }\n    }\n\n    pub fn is_time(data_type: &DataType) -> IsTimestamp {\n        match data_type {\n            DataType::FixedSizeList(field, 1) if field.name().starts_with(\"time:\") => {\n                IsTimestamp::Yes(TimePrecision::new(\n                    field\n                        .name()\n                        .strip_prefix(\"time:\")\n                        .expect(\"string prefix checked\")\n                        .parse::<u8>()\n                        .expect(\"invalid serialized time precision\"),\n                ))\n            }\n            _ => IsTimestamp::No,\n        }\n    }\n\n    pub fn is_timestamp_ntz(data_type: &DataType) -> IsTimestamp {\n        match data_type {\n            DataType::FixedSizeList(field, 1) if field.name().starts_with(\"timestamp_ntz:\") => {\n                IsTimestamp::Yes(TimePrecision::new(\n                    field\n                        .name()\n                        .strip_prefix(\"timestamp_ntz:\")\n                        .expect(\"string prefix checked\")\n                        .parse::<u8>()\n                        .expect(\"invalid serialized timestamp precision\"),","sourceCodeStart":1049,"sourceCodeEnd":1085,"githubUrl":"https://github.com/dbt-labs/dbt-core/blob/0267ce9170576975b76b64ce856b2e5848e96617/crates/dbt-adapter/src/sql_types.rs#L1049-L1085","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["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.","If you recently edited the match guard, restore the exact prefix literal in `strip_prefix` (e.g. \"time:\") so it matches the guard.","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."],"exampleFix":"// before\nDataType::FixedSizeList(field, 1) if field.name().starts_with(\"time:\") => {\n    IsTimestamp::Yes(TimePrecision::new(\n        field.name().strip_prefix(\"time:\").expect(\"string prefix checked\")...\n    ))\n}\n// after\nDataType::FixedSizeList(field, 1) => {\n    let Some(name) = field.name().strip_prefix(\"time:\") else { return IsTimestamp::No };\n    IsTimestamp::Yes(TimePrecision::new(name.parse::<u8>().unwrap_or_else(\n        |_| panic!(\"invalid serialized time precision: {}\", field.name()))))\n}","handlingStrategy":"validation","validationCode":"fn is_safe_time_field(data_type: &DataType) -> bool {\n    matches!(data_type, DataType::FixedSizeList(f, 1))\n        && matches!(data_type, DataType::FixedSizeList(f, _) if f.name().starts_with(\"time:\"))\n        && f.name().strip_prefix(\"time:\").map(|s| s.parse::<u8>().is_ok()).unwrap_or(false)\n}","typeGuard":"fn time_precision_field(dt: &DataType) -> Option<u8> {\n    if let DataType::FixedSizeList(f, 1) = dt {\n        f.name().strip_prefix(\"time:\").and_then(|s| s.parse::<u8>().ok())\n    } else { None }\n}","tryCatchPattern":null,"preventionTips":["Validate Arrow field-name encodings at schema ingestion boundaries before calling is_* helpers.","Never hand-edit serialized field names in fixtures; regenerate them from the serializer.","Keep starts_with guards and strip_prefix literals in lockstep when refactoring."],"tags":["rust","panic","internal-invariant","arrow"],"backgroundTag":"internal-invariant-violation","analyzedSha":"0267ce9170576975b76b64ce856b2e5848e96617","analyzedAt":"2026-09-07T21:53:39.732Z","contentChangedAt":"2026-09-07T21:53:39.732Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}