{"record":{"id":"6970bc229048420a","repo":"dbt-labs/dbt-core","slug":"diff-of-two-dicts-requires-a-dict-b-argument","errorCode":null,"errorMessage":"diff_of_two_dicts requires a dict_b argument","messagePattern":"diff_of_two_dicts requires a dict_b argument","errorType":"validation","errorClass":"InvalidOperation","httpStatus":null,"severity":"error","filePath":"crates/dbt-jinja-utils/src/functions/base.rs","lineNumber":626,"sourceCode":"        }\n\n        let dict_a_arg = match (kwargs.get(\"dict_a\"), args.first()) {\n            (Ok(value), _) => value,\n            (_, Some(value)) => value,\n            _ => {\n                return Err(Error::new(\n                    ErrorKind::InvalidOperation,\n                    \"diff_of_two_dicts requires a dict_a argument\",\n                ));\n            }\n        }\n        .clone();\n\n        let dict_b_arg = match (kwargs.get(\"dict_b\"), args.get(1)) {\n            (Ok(value), _) => value,\n            (_, Some(value)) => value,\n            _ => {\n                return Err(Error::new(\n                    ErrorKind::InvalidOperation,\n                    \"diff_of_two_dicts requires a dict_b argument\",\n                ));\n            }\n        }\n        .clone();\n\n        let dict_a = parse_dict_of_lists(&dict_a_arg)?;\n        let dict_b = parse_dict_of_lists(&dict_b_arg)?;\n\n        // Convert dict_b to lowercase for case-insensitive comparison.\n        // IndexMap preserves insertion order so `diff_of_two_dicts` matches\n        // Python's dict semantics (iteration follows dict_a's insertion order),\n        // which apply_grants macros rely on for deterministic REVOKE/GRANT order.\n        let mut dict_b_lowered: IndexMap<String, Vec<String>> = IndexMap::new();\n        for (key, value_list) in dict_b {\n            dict_b_lowered.insert(\n                key.to_lowercase(),","sourceCodeStart":608,"sourceCodeEnd":644,"githubUrl":"https://github.com/dbt-labs/dbt-core/blob/0267ce9170576975b76b64ce856b2e5848e96617/crates/dbt-jinja-utils/src/functions/base.rs#L608-L644","documentation":"diff_of_two_dicts validates that a value for dict_b is available, either via the dict_b kwarg or the second positional argument. When neither is present the function throws this InvalidOperation error. It complements the dict_a check at base.rs:614.","triggerScenarios":"Calling diff_of_two_dicts(a) with a single positional dict and no dict_b kwarg — the most common shape that reaches this branch. Also diff_of_two_dicts(dict_a=a) with only the dict_a kwarg.","commonSituations":"Developers assuming the function diffs against an implicit empty dict or the model config; partial refactors where the second dict argument was deleted; passing only one dict to compare 'with nothing'.","solutions":["Supply the second dict positionally: diff_of_two_dicts(a, b)","Or pass dict_b=... explicitly alongside dict_a","If comparing against an empty dict intentionally, pass {} as the second argument"],"exampleFix":"// before\n{% set diff = diff_of_two_dicts(my_dict) %}\n// after\n{% set diff = diff_of_two_dicts(my_dict, {}) %}","handlingStrategy":"validation","validationCode":"{% if dict_b is not defined %}\n  {{ exceptions.raise_compiler_error('dict_b is required for diff_of_two_dicts in ' ~ this) }}\n{% endif %}","typeGuard":"{% macro is_dict(x) %}{{ return(x is defined and x is mapping) }}{% endmacro %}","tryCatchPattern":null,"preventionTips":["Never call diff_of_two_dicts with a single dict; pass {} explicitly to diff against empty","Verify the second argument survived refactors","Use named kwargs to make both dicts obvious at the call site"],"tags":["jinja","dbt","missing-argument"],"backgroundTag":"missing-required-argument","analyzedSha":"0267ce9170576975b76b64ce856b2e5848e96617","analyzedAt":"2026-09-07T21:53:39.732Z","contentChangedAt":"2026-09-07T21:53:39.732Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}