Setup: the demo’s Orders schema, which every example on this page uses
import dagster as dg
import dataframely as dy
import polars as pl
import dagster_dataframely as dd
from dagster_dataframely_demo.schema import Orders
Every error this package raises is in dd.errors and subclasses dd.errors.DagsterDataframelyError. Catch one by name, or catch DagsterDataframelyError to catch every one. Every message names what failed and how to fix it.
| CollectionNotSupportedError |
at decoration |
pass a dy.Schema; declare one asset per Collection member, each with the member’s own schema |
| ReservedColumnError |
at decoration, and by every public function that takes a schema |
rename the column that starts with dy_ |
| InvalidColumnNameError |
the same |
rename the column, or change the alias= that sets its name, so the name uses only A-Za-z0-9_, the characters Dagster allows in a check name |
| CheckNameCollisionError |
the same |
rename one of the two rules that produce the same check name |
| InvalidSettingError |
when the package resolves the setting |
fix the value at the source the message names |
| MaterializeResultValueError |
before the column-schema check |
set value= to the frame; or return the frame and call context.add_asset_metadata; or write a plain @dg.asset and call dd.wiring.schema_metadata |
| MaterializeResultFieldError |
the same |
remove asset_key= or check_results=, which the decorator sets itself |
| ColumnSchemaError |
after the column-schema check fails |
fix the function that produced the frame, or cast with Schema.cast in the asset body |
| ValidationAbortError |
after Schema.filter, when rows failed and the asset declares no quarantine |
fix the rows upstream, write them to a quarantine with quarantine=True, or drop them in the asset body |
| NoValidRowsError |
after Schema.filter, when every row failed validation and the asset declares a quarantine |
read the invalid rows at the quarantine address in the message |
| QuarantineKeyCollisionError |
before the decorated function runs, on every run of an asset with quarantine=True |
rename the other asset, or remove quarantine=True; if the other asset is your quarantine table, delete it and use quarantine_spec |
| QuarantineDirError |
under direct invocation only, when there are invalid rows to write |
set DAGSTER_DATAFRAMELY_QUARANTINE_DIR, or run the asset |
ColumnSchemaError names every mismatched column at once, with its expected and actual dtype. The failing check’s dy_schema__errors table lists the same columns.
ValidationAbortError and NoValidRowsError both give the failure count per rule. The counts can add up to more than the number of invalid rows, because one row can fail several rules.
Three ways to get this wrong
Do not declare an asset keyed <name>_quarantine if <name> has quarantine=True. orders with quarantine=True writes its invalid rows to the asset key orders_quarantine, through the same IO manager that writes orders. An asset of your own keyed orders_quarantine has the same address. Both assets would write to the same table or file, and the second write would replace the first. So every run of orders checks for such an asset before the decorated function runs, and raises:
QuarantineKeyCollisionError: 'orders' declares `quarantine=True`, so its invalid rows are written
to 'orders_quarantine', which another asset in this code location already materializes. Rename
that asset, or remove `quarantine=True` from 'orders'. If that asset is your own quarantine table,
delete it and use `quarantine_spec` instead, which adds the quarantine itself to the asset graph.
A from __future__ import annotations in your own module breaks an annotated context parameter. Under PEP 563, Dagster receives every annotation as a string. Dagster compares the context annotation with the real classes, so it raises this error for both context: dg.AssetExecutionContext and context: AssetExecutionContext:
DagsterInvalidDefinitionError: Cannot annotate `context` parameter with type dg.AssetExecutionContext.
`context` must be annotated with AssetExecutionContext, AssetCheckExecutionContext, OpExecutionContext, or left blank.
The restriction is Dagster’s, not this package’s. @dg.asset raises the same error for the same annotation. Both accept an unannotated context, which is the only option in that message that still works under PEP 563:
@dd.asset(Orders)
def orders(context) -> pl.DataFrame:
context.log.info("run %s", context.run_id)
return pl.read_parquet("raw/orders.parquet")
A @dy.rule() body needs its class parameter. Without it, Python still defines the schema class and the asset, and raises no error. The run then fails when this package reads the rule’s expression for the check metadata:
TypeError: Orders.amount_is_positive() takes 0 positional arguments but 1 was given
@dy.rule() is a classmethod-style decorator, so the body takes cls:
class Orders(dy.Schema):
status = dy.Enum(["new", "paid", "shipped", "cancelled"], nullable=False)
amount = dy.Decimal(10, 2, nullable=False)
@dy.rule()
def paid_orders_have_amount(cls) -> pl.Expr:
"""Require a positive amount on a paid line."""
return (cls.status.col != "paid") | (cls.amount.col > 0)
The docstring becomes that check’s description in the catalog.