---
title: "dbt key columns: no not_null test | Metacenta"
description: "A null key silently drops rows from every inner join downstream. How to spot dbt models with no not_null test, and when a nullable key is right."
url: "https://metacenta.com/rules/model-no-not-null-test"
updated: "2026-10-14"
tokens: 677
---

Testing · rule model-no-not-null-test

# dbt key columns: no not_null test

A Metacenta review checks this under the rule **Tested models assert key columns are populated**. Everything below applies whether or not you ever commission one.

## What this rule checks

Among models that have tests, this rule flags those with no `not_null` test on any column. dbt_expectations' `expect_column_values_to_not_be_null` also counts. A test limited by `where` or `row_condition` doesn't. The manifest doesn't say which column is the key, so any whole-model `not_null` test passes.

## Why it matters

A null key drops rows silently from every inner join downstream. The totals still look plausible, so nobody notices until a figure fails to reconcile.

## How to fix it

**Test that key columns are never empty.** Add `not_null` to the columns that must always be populated: the grain column, and any column other models join on.

Before:

```yaml
      - name: customer_id
        data_tests:
          - relationships:
              to: ref('dim_customers')
              field: customer_id
```

After:

```yaml
      - name: customer_id
        data_tests:
          - not_null
          - relationships:
              to: ref('dim_customers')
              field: customer_id
```

## When it is fine to leave

A key that can be null by design, such as an optional foreign key on a fact table, should not carry `not_null`. Test the columns that must be populated instead. This rule is low severity for that reason: the gap is real, but the right column to test is a judgement.

## What we need to check it

`manifest.json` alone.

## Published rules it corresponds to

**[dbt_project_evaluator](https://dbt-labs.github.io/dbt-project-evaluator/)**, rule `fct_missing_primary_key_tests`. Ours checks a subset of what it flags; theirs is stricter. the other half of dbt Labs' rule, whose uniqueness requirement is `model-grain-not-asserted` here.

This means our check corresponds to their rule. It does not mean the publisher reviewed or endorses it.

[Ask about a review](https://metacenta.com/enquire?src=rule-model-no-not-null-test&via=md)

## Related pages

- [dbt models with no tests](https://metacenta.com/rules/model-untested)
- [dbt unique_combination_of_columns: how to use it](https://metacenta.com/rules/model-grain-not-asserted)
- [SQL tables with no assertion](https://metacenta.com/rules/sql-relation-untested)
- [Dagster asset checks: how to set them up](https://metacenta.com/rules/dagster-asset-unchecked)
- [A sample review, as a client reads it](https://metacenta.com/sample)
- [All published rules](https://metacenta.com/rules)
- [How a review is assessed](https://metacenta.com/standard)
