# Errors


Setup: the demo's `Orders` schema, which every example on this page uses

``` python
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](../reference/errors.DagsterDataframelyError.md#dagster_dataframely.errors.DagsterDataframelyError) to catch every one. Every message names what failed and how to fix it.

| error | raised | what to do |
|----|----|----|
| [CollectionNotSupportedError](../reference/errors.CollectionNotSupportedError.md#dagster_dataframely.errors.CollectionNotSupportedError) | at decoration | pass a `dy.Schema`; declare one asset per Collection member, each with the member's own schema |
| [ReservedColumnError](../reference/errors.ReservedColumnError.md#dagster_dataframely.errors.ReservedColumnError) | at decoration, and by every public function that takes a schema | rename the column that starts with `dy_` |
| [InvalidColumnNameError](../reference/errors.InvalidColumnNameError.md#dagster_dataframely.errors.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](../reference/errors.CheckNameCollisionError.md#dagster_dataframely.errors.CheckNameCollisionError) | the same | rename one of the two rules that produce the same check name |
| [InvalidSettingError](../reference/errors.InvalidSettingError.md#dagster_dataframely.errors.InvalidSettingError) | when the package resolves the setting | fix the value at the source the message names |
| [MaterializeResultValueError](../reference/errors.MaterializeResultValueError.md#dagster_dataframely.errors.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](../reference/errors.MaterializeResultFieldError.md#dagster_dataframely.errors.MaterializeResultFieldError) | the same | remove `asset_key=` or `check_results=`, which the decorator sets itself |
| [ColumnSchemaError](../reference/errors.ColumnSchemaError.md#dagster_dataframely.errors.ColumnSchemaError) | after the column-schema check fails | fix the function that produced the frame, or cast with `Schema.cast` in the asset body |
| [ValidationAbortError](../reference/errors.ValidationAbortError.md#dagster_dataframely.errors.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](../reference/errors.NoValidRowsError.md#dagster_dataframely.errors.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](../reference/errors.QuarantineKeyCollisionError.md#dagster_dataframely.errors.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](../reference/quarantine_spec.md#dagster_dataframely.quarantine_spec) |
| [QuarantineDirError](../reference/errors.QuarantineDirError.md#dagster_dataframely.errors.QuarantineDirError) | under direct invocation only, when there are invalid rows to write | set `DAGSTER_DATAFRAMELY_QUARANTINE_DIR`, or run the asset |

[ColumnSchemaError](../reference/errors.ColumnSchemaError.md#dagster_dataframely.errors.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.


<figure class="figure">
<p><img src="../assets/images/error-column-schema.png" class="img-fluid figure-img" /></p>
<figcaption>A run log where the <code>quantity</code> column is <code>Int64</code>. The failing <code>dy_schema__columns</code> check has <code>dy_schema__errors</code>, which shows the expected and actual dtype. The step failure below it names the same column in the [ColumnSchemaError](../reference/errors.ColumnSchemaError.md#dagster_dataframely.errors.ColumnSchemaError) message.</figcaption>
</figure>


[ValidationAbortError](../reference/errors.ValidationAbortError.md#dagster_dataframely.errors.ValidationAbortError) and [NoValidRowsError](../reference/errors.NoValidRowsError.md#dagster_dataframely.errors.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:

``` text
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`:

``` text
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:


``` python
@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:

``` text
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`:


``` python
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.
