Hooks
Hooks
Run SQL or Python lifecycle hooks around model materialization.
Pre-hooks and post-hooks run before and after model materialization. Use SQL hooks for warehouse statements and Python hooks for runtime control flow, providers, queries, and skip decisions.
Project layout
Section titled “Project layout”SQL and Python hooks live in language-specific directories:
my_project/ models/ marts/ orders.sql hooks/ sql/ permissions/ grant_access.sql record_access.sql python/ notifications.pyDirectories below hooks/sql/ and hooks/python/ are organizational. They do not namespace resource names: hooks/sql/permissions/grant_access.sql is still invoked as sql("grant_access").
Choose a hook type
Section titled “Choose a hook type”Hook lists use three explicit entry types:
| Entry | Definition | Use |
|---|---|---|
inline_sql("...") |
SQL written directly in the model header | Short, model-specific SQL |
sql("name", args...) |
A reusable resource under hooks/sql/ |
Shared, parameterized SQL |
python("name", args...) |
A decorated function under hooks/python/ |
Control flow, providers, queries, or skips |
Bare SQL strings are not hook entries. The singular pre_hook and post_hook fields are also invalid; use pre_hooks and post_hooks with one of the three typed forms above. sql("...") always means a named SQL hook and never falls back to inline SQL.
Configure model hooks
Section titled “Configure model hooks”Models may mix SQL and Python entries in one ordered hook list:
MODEL ( materialized table, pre_hooks [ inline_sql("INSERT INTO audit.build_log VALUES ('starting')"), ], post_hooks [ sql( "grant_access", relation: "@@CTX:destination.qualified", role: "analyst_role", ), python( "notify_complete", channel: "#data-builds", ), ],);
SELECT 1 AS idEntries execute in the order authored, including when SQL and Python entries are mixed.
Lifecycle and failure timing
Section titled “Lifecycle and failure timing”Pre-hooks run before materialization. A failed pre-hook prevents materialization, and a Python pre-hook that returns ctx.skip(...) stops the remaining pre-hooks and prevents the model from running.
Post-hooks run after warehouse mutation and audits. They are not promotion gates: a failed post-hook marks the model run as failed, but the relation has already been created, promoted, or incrementally updated. Put logic that must prevent materialization in a pre-hook, contract, pre-promotion audit, or the materialization itself.
A Python post-hook that returns ctx.skip(...) stops the remaining post-hooks and changes the reported model result. mode="hard" blocks downstream nodes. A soft skip does not automatically block every downstream node; scheduler propagation also depends on the other upstream results.
Names and identity
Section titled “Names and identity”SQL and Python hook names share the project-wide resource namespace with models, sources, seeds, functions, loaders, tasks, assets, checks, and providers. Any collision fails discovery, including two same-stem SQL hook files in different directories or a SQL and Python hook with the same name.
The ordered hook list participates in each model’s version identity:
- Inline and named SQL hooks contribute their fully rendered statements. Named hooks also retain their resource name, definition, arguments, description, and source path in identity metadata.
- Python hooks contribute the invocation name, configured arguments, and a version hash derived from the decorated function, its transitive first-party dependencies, and decorator configuration.
- Changing a hook body, arguments, or order changes the consuming model’s identity and makes it stale for change-aware planning.
- Executed Python hooks record their own hook fingerprints after successful completion or an explicit skip.
Named SQL hooks compile into their consuming models rather than becoming independently scheduled nodes. Python hooks also run as part of their model’s lifecycle phase rather than as independently selected Python nodes.
Diagnostics
Section titled “Diagnostics”Discovery and compilation fail early for malformed definitions and invocations. Diagnostics include the resource path and, for model entries, the model name and indexed label such as post_hooks[1] sql("grant_access").
See the language-specific pages for definition, argument, validation, and runtime diagnostics: