{"record":{"id":"481ee15bf0493d55","repo":"dbt-labs/dbt-core","slug":"valid-test-name-pattern","errorCode":null,"errorMessage":"Valid test name pattern","messagePattern":"Valid test name pattern","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/dbt-parser/src/resolve/resolve_tests/persist_generic_data_tests.rs","lineNumber":1791,"sourceCode":"        Some(self.source_name.clone())\n    }\n\n    fn base_tests(&self) -> FsResult<Option<Vec<DataTests>>> {\n        base_tests_inner(\n            self.table.tests.as_deref(),\n            self.table.data_tests.as_deref(),\n        )\n    }\n\n    fn column_tests(&self) -> FsResult<Option<BTreeMap<String, ColumnTestEntry>>> {\n        column_tests_inner(&self.table.columns)\n    }\n}\n\n/// Normalizes a test name following the existing dbt behavior\n/// https://github.com/dbt-labs/dbt-core/blob/main/core/dbt/parser/generic_test_builders.py#L121-L122\nfn normalize_test_name(input: &str) -> FsResult<String> {\n    let name_pattern = Regex::new(r\"^([a-zA-Z_][0-9a-zA-Z_]*)+\").expect(\"Valid test name pattern\");\n    name_pattern\n        .captures(input)\n        .and_then(|caps| caps.get(1))\n        .map(|m| m.as_str().to_string())\n        .ok_or_else(|| fs_err!(ErrorCode::InvalidConfig, \"Invalid test name: {}\", input))\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use dbt_schemas::schemas::data_tests::{CustomTestInner, CustomTestMultiKey};\n    use serde_json::Value;\n    use std::collections::{BTreeMap, HashMap};\n\n    #[test]\n    fn test_generic_test_asset_path_disambiguates_name_collisions() {\n        // `not_null` on `orders.status_code` and on `orders_status.code` both flatten\n        // to the same generated name; the second asset must not reuse the first path.","sourceCodeStart":1773,"sourceCodeEnd":1809,"githubUrl":"https://github.com/dbt-labs/dbt-core/blob/0267ce9170576975b76b64ce856b2e5848e96617/crates/dbt-parser/src/resolve/resolve_tests/persist_generic_data_tests.rs#L1773-L1809","documentation":"This panic fires from `Regex::new(...).expect(\"Valid test name pattern\")` while building the pattern used by `normalize_test_name` to strip dbt test-name suffixes. The pattern `^([a-zA-Z_][0-9a-zA-Z_]*)+` is a compile-time constant in the source, so the expect is an internal invariant assertion: it can only panic if the regex literal is edited into an invalid form or the `regex` crate rejects it at runtime. End users should never be able to trigger it via input.","triggerScenarios":"Calling `normalize_test_name` (invoked when persisting generic data test names during test resolution) after the hardcoded regex literal in persist_generic_data_tests.rs has been modified to a syntactically invalid pattern, or (theoretically) a regex crate version that fails to compile this pattern.","commonSituations":"A developer edits the regex string constant and introduces a typo (unbalanced parenthesis, bad escape); upgrading the regex crate in a way that changes pattern syntax acceptance. Real users cannot cause this.","solutions":["Restore the original regex literal `^([a-zA-Z_][0-9a-zA-Z_]*)+` in normalize_test_name","Validate the pattern locally with `Regex::new(...)` in a unit test so CI catches an invalid literal before runtime","If the panic reproduces on stock code, verify the `regex` crate dependency version and rebuild with `cargo build -p dbt-parser`"],"exampleFix":"// before\nlet name_pattern = Regex::new(r\"^([a-zA-Z_][0-9a-zA-Z_]*)+\").expect(\"Valid test name pattern\");\n// after (typo introduced by an edit — corrected)\nlet name_pattern = Regex::new(r\"^([a-zA-Z_][0-9a-zA-Z_]*)+\").expect(\"Valid test name pattern\");","handlingStrategy":"validation","validationCode":"// validate user input before calling normalize_test_name\nfn is_valid_test_name(input: &str) -> bool {\n    !input.is_empty() && input.chars().next().map_or(false, |c| c.is_ascii_alphabetic() || c == '_')\n        && input.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')\n}","typeGuard":"fn is_valid_test_name(input: &str) -> bool {\n    Regex::new(r\"^[a-zA-Z_][0-9a-zA-Z_]*$\").map(|re| re.is_match(input)).unwrap_or(false)\n}","tryCatchPattern":null,"preventionTips":["Treat the regex literal as frozen; add a unit test asserting Regex::new on it succeeds","Keep the regex crate pinned and run cargo update deliberately","Any change to normalize_test_name should be covered by tests with valid and invalid inputs"],"tags":["regex","panic","parser"],"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"}