dbt-labs/dbt-core · warning

Invalid primary key column: {column_name}

Error message

Invalid primary key column: {column_name}

What it means

A warning raised by the Databricks primary_key constraint macro when a column name listed in the constraint's `columns` (or the column-level constraint's own name) does not exist in the model's `columns` dictionary. The macro looks up `model['columns'][column_name]`; a miss means the ALTER TABLE ... PRIMARY KEY statement would reference a nonexistent column, so that column is skipped and warned about. Compilation still succeeds.

Source

Thrown at crates/dbt-loader/src/dbt_macro_assets/dbt-databricks/macros/relations/constraints.sql:195

        {% set stmt = "alter table " ~ relation.render() ~ " change column " ~ quoted_name ~ " set not null " ~ (constraint.expression or "") ~ ";" %}
        {% do statements.append(stmt) %}
      {% else %}
        {{ exceptions.warn('not_null constraint on invalid column: ' ~ column_name) }}
      {% endif %}
    {% endfor %}
  {% elif type == 'primary_key' %}
    {% if constraint.get('warn_unenforced') %}
      {{ exceptions.warn("unenforced constraint type: " ~ type)}}
    {% endif %}
    {% set column_names = constraint.get('columns', []) %}
    {% if column and not column_names %}
      {% set column_names = [column['name']] %}
    {% endif %}
    {% set quoted_names = [] %}
    {% for column_name in column_names %}
      {% set column = model.get('columns', {}).get(column_name) %}
      {% if not column %}
        {{ exceptions.warn('Invalid primary key column: ' ~ column_name) }}
      {% else %}
        {% set quoted_name = adapter.quote(column['name']) %}
        {% do quoted_names.append(quoted_name) %}
      {% endif %}
    {% endfor %}

    {% set joined_names = quoted_names|join(", ") %}
    {% set pk_expression = constraint.get('expression') %}

    {% set name = constraint.get('name') %}
    {% if not name %}
      {% if local_md5 %}
        {{ exceptions.warn("Constraint of type " ~ type ~ " with no `name` provided. Generating hash instead for relation " ~ relation.identifier) }}
        {%- set hash_input = "primary_key;" ~ relation.identifier ~ ";" ~ column_names ~ ";" -%}
        {%- if pk_expression -%}
          {%- set hash_input = hash_input ~ pk_expression ~ ";" -%}
        {%- endif -%}
        {%- set name = local_md5(hash_input) -%}

View on GitHub (pinned to 0267ce9170)

Solutions

  1. Fix the column name in the constraint so it exactly matches a key under `columns:` in the model's YAML.
  2. Add the missing column definition (name plus any properties) under the model's `columns:` in schema.yml.
  3. Check case sensitivity: Databricks column names in YAML must match exactly as declared.
  4. Remove the stale column from the constraint's `columns` list if the column was dropped.

Example fix

// before (schema.yml)
columns:
  - name: user_id
constraints:
  - type: primary_key
    columns: [userId]
// after
constraints:
  - type: primary_key
    columns: [user_id]
Defensive patterns

Strategy: validation

Validate before calling

// pre-run schema validation (Python)
import yaml
cfg = yaml.safe_load(open('models/schema.yml'))
for m in cfg['models']:
    cols = {c['name'] for c in m.get('columns', [])}
    for c in m.get('constraints', []):
        if c['type'] == 'primary_key':
            missing = [n for n in c.get('columns', []) if n not in cols]
            assert not missing, f"{m['name']}: PK columns not in columns: {missing}"

Type guard

def has_column(model: dict, col: str) -> bool:
    return col in model.get('columns', {})

Prevention

When it happens

Trigger: A `primary_key` constraint references a column name that is misspelled, not defined under `columns:` in the model's YAML (schema.yml), or differs in case from the declared column; the lookup `model.get('columns', {}).get(column_name)` at constraints.sql:193 returns nothing.

Common situations: Typo in the constraint `columns` list; declaring a column-level PK constraint in dbt_project.yml or config instead of schema.yml so the column context is missing; renaming a column without updating the constraint; relying on columns inferred only from SQL (constraints need explicit `columns:` entries in YAML for this lookup).

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of dbt-labs/dbt-core@0267ce9170 (2026-09-07). Data as JSON: /api/errors/37c0b2a7971e84a7. Report an issue: GitHub.