ServicesAboutNotesContact Get in touch →
EN FR
Note

dbt Doc Block Jinja Limitations

What you cannot do inside dbt doc blocks — restricted Jinja context, the README parsing gotcha, and the missing column description inheritance feature

Planted
dbtdata engineering

Doc blocks look like they support Jinja because they use {% docs %} / {% enddocs %} tags and live in .md files that dbt’s Jinja parser processes. But the Jinja context inside a doc block is heavily restricted, and misunderstanding this causes errors that are confusing to debug.

The Restricted Context

Doc blocks are compiled with a minimal Jinja context. The key functions that are not available inside doc blocks:

  • ref() — you cannot reference models
  • source() — you cannot reference sources
  • config() — no configuration context
  • var() — no project variables
  • Most other dbt Jinja functions

The doc() function itself does work, meaning you can reference one doc block from within another. But that is essentially the only dbt-specific function available.

If you try something like this inside a doc block:

{% docs order_status %}
See {{ ref('stg_orders') }} for the raw status values.
{% enddocs %}

You will get a 'ref' is undefined error during compilation. The workaround for cross-referencing models is to use internal URL paths instead:

{% docs order_status %}
See [stg_orders](#!/model/model.my_project.stg_orders) for the raw status values.
{% enddocs %}

The README Parsing Gotcha

dbt’s Jinja parser processes all .md files in your resource paths, not just files containing doc blocks. This catches people off guard when they have a README.md in their models directory.

If your README contains anything that looks like Jinja syntax — {{ something }}, {% block %}, even code examples showing Jinja — the parser will try to evaluate it and fail with 'ref' is undefined or similar errors.

Two solutions:

  1. Move READMEs out of resource paths. Keep documentation in a top-level docs/ folder that is not listed in model-paths, seed-paths, or other resource path configurations.
  2. Avoid Jinja-like syntax in READMEs. If you must have a README in a models directory, use code blocks or escape any {{ / {% sequences. You can use {% raw %}...{% endraw %} to prevent Jinja processing of a block.

This gotcha also applies to any .md files pulled in by dbt packages. If a package includes Markdown files with Jinja-like syntax in its resource paths, those files will be parsed too.

The Missing Feature: Column Description Inheritance

One of the most requested features in dbt is column description inheritance — where downstream models automatically pick up column descriptions from their upstream parents. If you describe customer_id in your base model, every intermediate and mart model that uses that column would inherit the description without you doing anything.

This is tracked as GitHub issue #1158 and has not been implemented natively as of early 2026.

Without native inheritance, you have two options for avoiding description duplication:

Shared Doc Blocks

Define a doc block for each common column and reference it explicitly everywhere:

# In base model YAML
columns:
- name: customer_id
description: "{{ doc('customer_id') }}"
# In mart model YAML -- same reference
columns:
- name: customer_id
description: "{{ doc('customer_id') }}"

This requires you to add the {{ doc() }} reference in every model’s YAML, but at least the description text is defined in one place. Change the doc block and it updates everywhere.

dbt-osmosis Propagation

dbt-osmosis solves this more automatically. It traces column lineage through your DAG and copies descriptions from upstream models to downstream ones. Running dbt-osmosis yaml refactor propagates descriptions without requiring doc block references at all — it simply copies the text into each model’s YAML.

The tradeoff: dbt-osmosis duplicates the text (so each YAML file has its own copy of the description), while doc blocks keep a single source of truth. For most teams, dbt-osmosis is less maintenance because it runs automatically, but doc blocks give you stronger guarantees about consistency.

Practical Implications

These limitations shape how you architect your documentation:

  • Use doc blocks for the description text itself, and #!/model/... URL paths for cross-references between models
  • Keep non-doc-block Markdown files out of resource paths, or wrap their content in {% raw %} tags
  • For column description reuse, choose between shared doc blocks (single source of truth, more setup) and dbt-osmosis (automated propagation, duplicated text). Both solve the problem; they trade off differently