{"record":{"id":"1dbdaeff040238ac","repo":"dbt-labs/dbt-core","slug":"all-snapshot-macros-should-start-with-snapshot","errorCode":null,"errorMessage":"All snapshot macros should start with 'snapshot_'","messagePattern":"All snapshot macros should start with 'snapshot_'","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/dbt-parser/src/resolve/resolve_snapshots.rs","lineNumber":140,"sourceCode":"        stdfs::create_dir_all(&snapshots_dir)?;\n    }\n\n    // Save snapshots to the `snapshots` directory\n    let mut snapshot_files = Vec::new();\n    let mut sql_defined_snapshots = Vec::new();\n    // Map target path to original macro path for checksum recalculation\n    let mut snapshot_original_paths: HashMap<PathBuf, PathBuf> = HashMap::new();\n    let default_snapshots_path = vec![DBT_SNAPSHOTS_DIR_NAME.to_string()];\n    let mut synthetic_snapshot_macro_uids: HashSet<String> = HashSet::new();\n    for (macro_uid, macro_node) in macros {\n        if macro_node.package_name == package_name && macro_uid.starts_with(\"snapshot.\") {\n            synthetic_snapshot_macro_uids.insert(macro_uid.clone());\n            // Write the macro call to the `snapshots` directory\n            let macro_call = format!(\"{{{{ {}() }}}}\", macro_node.name);\n            let macro_name = macro_node.name.clone();\n            let snapshot_name = macro_name\n                .strip_prefix(\"snapshot_\")\n                .expect(\"All snapshot macros should start with 'snapshot_'\")\n                .to_string();\n\n            // Preserve file layout for proper fqn generation\n            let original_relative_path = strip_resource_paths_from_ref_path(\n                &macro_node.path,\n                package\n                    .dbt_project\n                    .snapshot_paths\n                    .as_ref()\n                    .unwrap_or(&default_snapshots_path),\n            );\n\n            let target_path = PathBuf::from(DBT_SNAPSHOTS_DIR_NAME)\n                .join(original_relative_path.with_file_name(format!(\"{snapshot_name}.sql\")));\n            let snapshot_path = arg.io.out_dir.join(&target_path);\n            if let Some(parent) = snapshot_path.parent() {\n                stdfs::create_dir_all(parent)?;\n            }","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/dbt-labs/dbt-core/blob/0267ce9170576975b76b64ce856b2e5848e96617/crates/dbt-parser/src/resolve/resolve_snapshots.rs#L122-L158","documentation":"This is a Rust panic raised by `str::strip_prefix(...).expect(...)` while converting internal 'snapshot.*' macros back into snapshot .sql files during parse-time resolution of a package's snapshots. The parser assumes every macro whose uid starts with `snapshot.` also has a macro *name* starting with the `snapshot_` prefix, so the derived snapshot name (the macro name minus that prefix) can be used as the output filename `snapshots/<name>.sql`. If a snapshot macro's name lacks the prefix, the invariant is broken and the parser panics instead of silently producing a misnamed file.","triggerScenarios":"Running `dbt parse`/a build when a macro with uid starting `snapshot.` in the target package has a name that does not begin with `snapshot_` — e.g. a user-defined macro named like a snapshot (uid `snapshot.<pkg>.<name>` where name lacks the `snapshot_` prefix), a hand-edited or stale `target/` manifest, or an adapter/tool that synthesizes snapshot macros with an unexpected naming scheme.","commonSituations":"Users renaming a snapshot macro file or macro name without keeping the `snapshot_` prefix; packages written for other dbt tooling that name snapshot macros differently; generated/templated macros from scripts; upgrading a project that previously had a custom snapshot-naming convention into this parser.","solutions":["Rename the offending macro so its name starts with `snapshot_` (the snapshot file will then be emitted as `snapshots/<name-without-prefix>.sql`)","Check for a stale `target/` directory and delete it so macros are re-parsed from source rather than from a stale manifest","If the macro is not a real snapshot, move it out of the snapshot path/namespace so its uid no longer starts with `snapshot.`","If this comes from a third-party package, report/patch the package or pin a version whose snapshot macros follow the `snapshot_` naming convention"],"exampleFix":"// before: macro name without required prefix\n{% macro my_snapshot() %} ... {% endmacro %}\n\n// after\n{% macro snapshot_my_snapshot() %} ... {% endmacro %}","handlingStrategy":"validation","validationCode":"// before invoking the parser / in CI\nfor macro_node in snapshot_macros {\n    if macro_node.uid.starts_with(\"snapshot.\") && !macro_node.name.starts_with(\"snapshot_\") {\n        return Err(format!(\"snapshot macro '{}' must be named 'snapshot_*'\", macro_node.name));\n    }\n}","typeGuard":"fn is_valid_snapshot_macro(name: &str) -> bool {\n    name.starts_with(\"snapshot_\") && name.len() > \"snapshot_\".len()\n}","tryCatchPattern":null,"preventionTips":["Always name snapshot macros with the `snapshot_` prefix; keep the prefix when renaming files or macros","Never hand-edit `target/` artifacts; delete and regenerate instead","Lint dbt packages in CI for macros whose uid starts with `snapshot.` but whose name lacks the prefix","Keep third-party packages on versions that follow the standard snapshot macro naming convention"],"tags":["rust","parser","snapshots","naming-invariant","panic"],"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"}