dct validate¶
Fast YAML schema and cross-reference validation. No DB hit, no execute (by default). Use it for sub-second checks in editors, pre-commit hooks, and CI. Add --warehouse to extend validation to the warehouse layer.
Arguments¶
| Argument | Description |
|---|---|
PATH |
Path to board YAML file or directory. Defaults to charts/ when omitted. |
Options¶
| Flag | Description |
|---|---|
--project-dir PATH |
Project root for resolving relative paths. |
--json |
Output as JSON. |
--strict / --no-strict |
Exit 1 on warnings. Default: off. |
--warehouse |
Validate queries against the warehouse using the cheapest per-adapter mechanism (see below). Requires resolvable credentials. |
Examples¶
dct validate # Validate all boards in charts/
dct validate charts/ # Validate all boards in a directory
dct validate charts/sales.yml # Validate one file
dct validate charts/sales.yml --json
dct validate charts/sales.yml --strict
dbt parse && dct validate # Validate with manifest-backed ref checks
dct validate charts/sales.yml --warehouse # Warehouse-level validation
When given a directory, dct validate globs **/*.yml and **/*.yaml, skipping any file whose name starts with _ (partials and templates).
What it validates¶
- YAML syntax
- Schema conformance (every field, every chart family, every query shape)
- Cross-references inside the board (chart
query:names exist,rows:references exist, variable references resolve) - Variable defaults and types
- Field-level constraints (enums, ranges, mutually exclusive options)
- Structural SQL lint on named
queries:entries (cartesian joins, missing join predicates); emitted as warnings; exit code 1 with--strict. Inline chart queries are skipped. ref()/source()names exist in the dbt manifest (when a manifest is present attarget/manifest.json)- Model column drift (manifest present): each dbt model's output columns are derived statically from its SQL, and every query column reaching that model through
ref()is checked against them: a renamed or dropped column fails withERR-DBT-MODEL-COLUMN-MISSINGbeforedbt runrebuilds the warehouse. A model or query whose columns cannot be derived (SELECT *, macros in projection position, seeds, snapshots) is reported as a warning, never silently passed.
--warehouse validation¶
Adds a warehouse-level check per query using the cheapest available mechanism:
| Adapter | Mechanism | What it checks |
|---|---|---|
| DuckDB | DESCRIBE |
Validity + result column schema; schema only, no billing |
| csv/json/parquet | DESCRIBE |
Result column schema only — not validity: the source must materialize its files first (read and parse them), and a materialization failure is reported unchecked, not invalid |
| BigQuery | Native dry-run | Validity + result column schema; unbilled (QueryJobConfig(dry_run=True)) |
| Postgres | EXPLAIN |
Validity only; the query parses, binds and plans; no result schema (WARN-COLUMN-CHECK-UNAVAILABLE) |
| Redshift | EXPLAIN |
Validity only, as above |
| Snowflake | EXPLAIN |
Validity only, as above; compiled by the cloud services layer, no running warehouse needed |
| Everything else | None | Nothing; reported as unchecked (WARN-WAREHOUSE-CHECK-UNAVAILABLE) |
The last row is the important one: an adapter with no primitive that runs without executing the query is reported as unchecked, not as passing. --warehouse will never run your query at full cost to find out whether it works. Databricks sits in that row deliberately even though it has an EXPLAIN: Spark returns planner errors as plan text instead of failing the statement, so an EXPLAIN check there would report a broken query as valid.
Two other things report as unchecked, each with the reason in the warning message:
- a query composing another query's cached result (
{{ queries.x.cache }}): that result only exists at render time, so no warehouse has heard of it - a warehouse that could not be reached (bad host, expired credentials): a fault raised before any SQL reaches the warehouse says nothing about your query, so it is never reported as an invalid query
A query naming no source is different: it is a project configuration fault, not a per-query warning, so it is reported as an error and stops the sweep immediately instead of being reported as unchecked.
Under --strict, every one of those warnings exits 1; that is the mode to use in CI when "unchecked" is not good enough.
When column schema is available (DuckDB, csv/json/parquet, BigQuery; the EXPLAIN adapters prove validity but return no schema), --warehouse also checks that every chart's channel fields (x, y, color, theta, etc., including those on layers:) reference a column the query actually returns.
--warehouse is additive: the full stateless pass above runs first and its findings are always reported. Queries are checked exactly as dct render would run them (dbt ref()s resolved, setup_sql on the same connection, board variable defaults applied) so a check never fails on SQL your render would never send.
What it does not check (without --warehouse)¶
- Whether referenced dbt models / tables actually exist in the warehouse
- Whether queries execute successfully
- Whether columns return rows
- Chart-render output
For those, use dct validate --warehouse or dct render.
Related¶
dct describe: describe a dashboard's queries, charts, variables, and layout- Error Handling Guide