[v2 Bug] dbt lint: CP02 (dbt0167) silently falls back to "consistent" when extended_capitalisation_policy is "snake" or "camel"
- Dominant language
- Rust
- Stars
- 13.8k
- Forks
- 2.6k
- Avg merge
- 21h 31m
- Merged PRs (30d)
- 56
Description
### Is this a new bug in dbt v2.x compared to the latest version of dbt 1.x?
- [x] I believe this is a new bug in dbt v2.x
- [x] I have searched the existing issues and could not find a duplicate
### Current Behavior
`dbt lint` reads `extended_capitalisation_policy` under `[sqlfluff:rules:capitalisation.identifiers]` for `consistent`, `lower`, `upper`, `capitalise` and `pascal`, but **silently falls back to `consistent`** when the value is `snake` or `camel`. Both are valid SQLFluff values for CP02 (SQLFluff's `_handle_segment` handles `snake` / `camel` explicitly, see `sqlfluff/rules/capitalisation/CP01.py`).
There is no warning that the value is unsupported. The only hint is that the violation message says "expected to be **Consistent** across the query" even though the config says `snake`.
Side note: with `capitalise`, the message says "expected to be **Pascal**". The violation set matched SQLFluff in my test, so this may be a label-only issue, but it suggests the policy mapping is lossy.
The practical impact: a project that standardises on `snake` gets `IdentifierCaseMismatch` errors on every model that mixes snake_case identifiers with camelCase source columns (which cannot be renamed). SQLFluff on the same config reports the camelCase columns only, which is the intended signal.
### Expected Behavior
`extended_capitalisation_policy = snake` should enforce snake_case (flag `metadataJson`, accept `metadata_json`), matching SQLFluff:
```
L: 2 | P: 7 | CP02 | Unquoted identifiers must be snake case.
L: 3 | P: 7 | CP02 | Unquoted identifiers must be snake case.
```
Same for `camel`. If a policy value is not implemented, `dbt lint` should emit an explicit warning (e.g. "unsupported extended_capitalisation_policy 'snake', falling back to 'consistent'") instead of silently changing semantics.
### Steps To Reproduce
1. Create a minimal project with one model `models/policy_repro.sql`:
```sql
SELECT
src.metadataJson AS metadata_json,
src.createdAt AS created_at,
src.deal_id AS deal_id
FROM raw_deals AS src
```
2. `.sqlfluff` in the project root:
```ini
[sqlfluff]
dialect = bigquery
templater = jinja
rules = CP02
[sqlfluff:rules:capitalisation.identifiers]
extended_capitalisation_policy = snake
```
3. Run `dbt lint --select policy_repro` and compare with `sqlfluff lint models/policy_repro.sql`.
4. Repeat with `extended_capitalisation_policy = camel`, then with `lower` (control: this one works).
### Relevant log output
```shell
# policy = snake
$ dbt lint --select policy_repro
[warning] [IdentifierCaseMismatch (dbt0167)]: Identifier case is expected to be Consistent across the query, got a mix of Lower and Lower ('src', 'metadata_json') [CP02].
[error] [IdentifierCaseMismatch (dbt0167)]: Identifier case is expected to be Consistent across the query, got a mix of Lower and Camel ('src', 'createdAt') [CP02].
Finished 'lint' with 2 warnings and 1 error for target 'dev'
$ sqlfluff lint models/policy_repro.sql
L: 2 | P: 7 | CP02 | Unquoted identifiers must be snake case.
L: 3 | P: 7 | CP02 | Unquoted identifiers must be snake case.
# policy = camel
$ dbt lint --select policy_repro
[warning] [IdentifierCaseMismatch (dbt0167)]: Identifier case is expected to be Consistent across the query, got a mix of Lower and Lower ('src', 'metadata_json') [CP02].
[error] [IdentifierCaseMismatch (dbt0167)]: Identifier case is expected to be Consistent across the query, got a mix of Lower and Camel ('src', 'createdAt') [CP02].
$ sqlfluff lint models/policy_repro.sql
All Finished! # no violations
# policy = lower (control — works as expected)
$ dbt lint --select policy_repro
[error] [IdentifierCaseMismatch (dbt0167)]: Identifier case is expected to be Lower across the query, got Camel ('metadataJson') [CP02].
[error] [IdentifierCaseMismatch (dbt0167)]: Identifier case is expected to be Lower across the query, got Camel ('createdAt') [CP02].
$ sqlfluff lint models/policy_repro.sql
L: 2 | P: 7 | CP02 | Unquoted identifiers must be lower case.
L: 3 | P: 7 | CP02 | Unquoted identifiers must be lower case.
```
Full matrix (`consistent` / `lower` / `upper` / `capitalise` / `pascal` / `camel` / `snake`):
| policy | dbt lint evaluates as | SQLFluff 4.2.2 | parity |
|---|---|---|---|
| consistent | Consistent | consistently lower | OK |
| lower | Lower | lower | OK |
| upper | Upper (11) | upper (11) | OK |
| capitalise | **Pascal** (11) | capitalised (11) | same set, label differs |
| pascal | Pascal | pascal | OK |
| camel | **Consistent** | camel (0 violations) | **mismatch** |
| snake | **Consistent** | snake (2 violations) | **mismatch** |
### Environment
```markdown
- OS: macOS 15 (Darwin 25.6.0), Apple Silicon
- CPU: ARM
- dbt distribution and version: dbt-fusion 2.0.0-preview.209
- SQLFluff used for comparison: 4.2.2
```
### Which database adapter are you using?
- [x] bigquery
### Is this a discrepancy vs. dbt 1.x?
Yes. dbt 1.x projects lint with SQLFluff directly (pre-commit / `dbt sqlfluff lint`), which honours `snake` and `camel`. Moving the same `.sqlfluff` to `dbt lint` on Fusion changes the rule semantics without any warning.
### Additional Context
- The unrelated `[warning] ... got a mix of Lower and Lower ('src', 'metadata_json')` line under `consistent` is self-contradictory (two Lower identifiers reported as a mismatch). It appears whenever the policy resolves to `consistent`; possibly worth a separate look.
- Related: #15240 (date-part false positive), #15852 (CAST type-name false positive). This issue is about policy handling, not token classification.
- Companion issues: `-- noqa: disable=` / `enable=` range directives are ignored by `dbt lint` (https://github.com/dbt-labs/dbt-core/issues/16175), and BigQuery `DATE_TRUNC` / `LAST_DAY` date parts are evaluated as identifiers (https://github.com/dbt-labs/dbt-core/issues/16176). The three together made CP02 unusable for us (165 permanent false positives in CI), so we had to exclude CP02 entirely.
Contributor guide
Assessment
This issue has not been assessed yet.