Skip to main content

About dbt lint command Beta

dbt lint is a fast SQL linter built into dbt v2, available locally or in dbt platform. dbt lint requires v2 or later. If you're on an earlier version, upgrade or install dbt.

It's SQLFluff-compatible: it reads your .sqlfluff config, uses the same rule codes (for example, CP01, RF03), and respects -- noqa suppression comments. Compatible doesn't mean identical: dbt lint and SQLFluff can return different results for the same file and config. Refer to Rule parity with SQLFluff.

You can use your existing SQLFluff config with minimal changes. dbt Labs intends to track the latest SQLFluff rule spec going forward.

note

dbt lint is part of dbt v2 and is not the same as dbt sqlfluff lint on the dbt platform CLI. For SQLFluff on the platform CLI, refer to Configure the dbt platform CLI. Linting in Studio IDE continues to use SQLFluff.

Benchmarks

Across project sizes from 1k to 10k models, dbt lint runs 40×–250× faster than SQLFluff with all cores enabled and 280×–1500× faster than single-threaded SQLFluff.

dbt Labs ran these benchmarks on SQLFluff 4.2.1 against dbt projects on the Snowflake dialect, ranging from 1k to 10k models, on a MacBook Pro with a 12-core Apple M4 Pro and 24 GB of RAM.

Usage

dbt lint [FILE] [flags]

[FILE] is optional. When you omit [FILE], dbt lint lints all SQL files in your project.

Flags

FlagDescription
--fixAutomatically apply fixes for auto-fixable rule violations. See Rules without autofix for rules that cannot be fixed automatically.
--config <path>Path to a .sqlfluff config file. Overrides auto-discovery.
--rulesComma-separated list of rule codes to enable. Overrides config.
--exclude-rulesComma-separated list of rule codes to disable. Overrides config.
--changedLint only files modified in the current git working tree.
--format human|json|github-annotationOutput format. Defaults to human. Use json for machine-readable output or github-annotation for GitHub Actions integration.
--jinja-render-mode <mode>How dbt lint renders Jinja before linting. Accepts symbolic (default), rendered, or turbo. Overrides .sqlfluff config. Refer to Jinja render modes.

Configuration

dbt lint auto-discovers the nearest .sqlfluff file in your project directory tree. CLI flags --rules and --exclude-rules take precedence over the values in the config file. To create a .sqlfluff file, see SQLFluff configuration files.

Jinja render modes

Before dbt lint can check a model, it has to turn your Jinja-templated SQL into plain SQL. Most Jinja renders cleanly at lint time, but some macros ask your data platform a question, such as which columns a table has, and dbt lint never connects to your platform, so those calls have no real answer. The jinja_render_mode setting controls how dbt lint handles them, and that changes which violations you see.

Most projects should keep the default, symbolic. The three modes are:

ModeSummary
symbolic (default)Renders your Jinja normally and substitutes a placeholder for results it can't get from your platform.
renderedRenders your Jinja against empty stub values, with no signal that they're stand-ins.
turboSkips execution entirely and lints your literal template text.

Symbolic (default)

Executes your Jinja, but tracks which values come from introspective adapter calls, such as adapter.execute, adapter.get_relation, and adapter.get_columns_in_relation. Because those calls can't reach your warehouse at lint time, dbt lint replaces their output with a placeholder instead of a misleading empty value. Everything else renders normally.

Keep the default. It produces the fewest false positives on projects that use introspective macros.

For example, a model that loops over the result of an introspective call:

{% set cols = adapter.get_columns_in_relation(ref('orders')) %}
select {{ cols | map(attribute='name') | join(', ') }}
from {{ ref('orders') }}

renders, for linting purposes, as something like:

select your_columns
from orders

dbt lint doesn't report violations against the placeholder value itself, but it lints everything else in the query normally.

Rendered

Executes your Jinja against parse-time stub values. Introspective adapter calls return empty results with no signal that the values aren't real, so the same model renders as:

select
from orders

The empty select list can produce SQL your project would never run, and dbt lint checks that unrealistic result instead. Compare rendered against symbolic when you're investigating an unexpected violation.

Turbo

Never executes your Jinja. It reads the template syntactically, keeps the literal SQL you wrote, and replaces every {{ ... }} expression with a placeholder, including ones that aren't introspective:

select your_expression
from your_expression

Use it when rendering is too slow or fails outright on a model. It's the fastest mode, but it can't see anything a macro generates.

Set the render mode

Set the mode for a single run with --jinja-render-mode. The flag works with both dbt lint and dbt format:

dbt lint --jinja-render-mode rendered
dbt format --jinja-render-mode turbo

Set it for the whole project in the [dbt] section of your .sqlfluff file:

[dbt]
jinja_render_mode = rendered

The CLI flag takes precedence over the config file.

Render variants

In symbolic and turbo modes, a single model can produce more than one candidate SQL output. When dbt lint reaches an {% if %} block whose condition it can't resolve, it lints more than one branch rather than guessing which one you meant. Each candidate is a render variant, and dbt lint reports the violations it finds across all of them.

render_variant_limit caps how many variants dbt lint produces per model, and defaults to 5. Set it in the [sqlfluff] section of your .sqlfluff file:

[sqlfluff]
render_variant_limit = 10

Raising the limit widens coverage at the cost of lint time, because each additional variant is another render of the template. Lowering it to 1 restricts dbt lint to a single variant per model.

Ignoring files and directories

Use a .sqlfluffignore file at your project root to exclude paths you aren't ready to lint yet, such as dbt_packages/ or models/legacy/.

.sqlfluffignore uses .gitignore-style syntax. For the full pattern reference, see the SQLFluff .sqlfluffignore documentation.

# .sqlfluffignore
dbt_packages/
models/legacy/
snapshots/

When you're ready to lint those paths, remove their entries from .sqlfluffignore.

Reducing noise in the Studio IDE Problems tab

The Studio IDE lints SQL automatically and surfaces violations in the Problems tab. If you see a large number of style warnings and aren't ready to address them, add your model directories to .sqlfluffignore to remove those violations from the Problems tab immediately. Remove the ignore entries incrementally as you clean up violations.

Suppressing violations

dbt lint supports the full SQLFluff suppression syntax:

SuppressionScope
-- noqaSuppress all violations on the line
-- noqa: CP01, RF03Suppress specific rules on this line
-- noqa-fileSuppress all violations in the file
-- sqlfluff:disable CP01Disable a rule in the file

Supported dialects

The following dialects are currently supported with dbt lint:

  • Snowflake
  • BigQuery
  • DuckDB
  • Redshift
  • Databricks
  • SparkSQL (currently aliased to Databricks)

Additional dialect support is coming soon.

dbt format

dbt format (also available as dbt fmt) automatically formats your SQL files according to the layout (LT*) rules in your .sqlfluff file. Unlike dbt lint, it doesn't issue diagnostics. It applies fixes silently and in place when you run the command.

dbt format [FILE] [flags]
dbt fmt [FILE] [flags]

[FILE] is optional. When omitted, dbt format formats all SQL files in your project.

Rule parity with SQLFluff

dbt lint aims for high overlap with SQLFluff, but it doesn't guarantee rule-for-rule parity, and small differences will always exist. Layout and indentation rules, such as LT02, are one known area of difference.

Because linting in the Studio IDE still uses SQLFluff, Studio IDE Lint file and dbt lint can report different violations for the same project code. Similarly, CI jobs on a dbt v2 version invoke dbt lint instead of SQLFluff, so results from CI jobs can differ from your SQLFluff results.

If you need SQLFluff behavior, you can either lint in the Studio IDE, which continues to run SQLFluff, or run SQLFluff locally using the standalone dbt v1 engine templater. Refer to dbt v2 limitations for more information.

Beta limitations

Keep these limitations in mind:

Rules without autofix

The following rules report violations but can't be auto-fixed by --fix. They require reordering of SQL fragments or broader reflow that source-mapping (based on macro_spans) can't safely fix inside Jinja-templated SQL:

  • Aliasing: AL03, AL04, AL06, AL08
  • References: RF01, RF02, RF04, RF05
  • Structure: ST03, ST04, ST05, ST06, ST07, ST09, ST10, ST11
  • Ambiguity / convention: AM01, AM06, CV08, CV09, CV12

Single fix pass

--fix runs a single pass; it doesn't iterate until the file is clean. A fix applied by one rule can expose a violation from another rule on the next run. For example, AL09 removes a self-alias, which may then cause RF02 to flag the now-unqualified reference. Re-run dbt lint --fix until the output is clean.

FAQs

 Why doesn't dbt lint check every possible output of my Jinja?
 Why doesn't dbt lint report violations from some macros?

Feedback

If you encounter unexpected behavior or have suggestions, open an issue in the dbt-labs/dbt GitHub repository and apply the Linter label.

Was this page helpful?

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

0
Loading