# Failures and diagnostics Let lifecycle and Pydantic errors surface at their natural boundary: | Error | Meaning | | --- | --- | | `UnsetError` | A draft field was read before it had a usable value | | `DraftError` | A draft was copied or sent through Pydantic serialization, or a final was finalized again | | `TemplateError` | An unbound template was used as though it had validated field values | | Pydantic `ValidationError` | Final input, interpolation output, or a structural Config position was invalid | | `TypeError` | A Config class disabled a lifecycle invariant or declared an unsupported schema shape | | `AttributeError` | A draft write used an unknown field or interpolation read unavailable context | ## Missing draft values Required scalar reads fail immediately. Required direct Config fields create a child draft, but a recursive required spine without a base case fails rather than recursing forever. Pending interpolation can be assigned and finalized but cannot be read from a draft. ## Interpolation failures Interpolation errors report the target path and callable site. Common causes are: - reading a later field; - selecting an ancestor above the current root; - requesting the wrong typed context model; - returning an active incomplete branch; - mutating a read-only built-in container view; - raising inside the user callable. Reorder source fields before dependent fields. For a default child that needs parent context, use the direct `child: Child = Child()` spelling. The standalone child constructor becomes an unbound template and binds in the parent's field pipeline. Do not use a draft-returning default factory. An unbound template is created only by normal `Config(...)` construction when at least one interpolation lacks an ancestor/root/nearest context or raises `NameError`, with no other errors except omitted required fields. A repeated `NameError` during binding is reported as a normal interpolation failure. Other Pydantic validation entry points never return templates. ## Structural graph failures Draft collection and template binding follow concrete annotations through Config fields and supported built-in containers. A Config under `Any`, `object`, a wrong union branch, or an incompatible tuple or mapping position is rejected. Markers inside built-in containers are also rejected. Arbitrary user objects are opaque and are not crawled for drafts or markers. If such an object needs lifecycle-aware children, move those children into directly annotated Config fields. ## Native Pydantic footguns `model_copy(update=...)` does not validate updates. Model-after validators and `model_post_init` may mutate values after interpolation. Shallowly frozen finals may still have mutable container contents. Revalidate current concrete values with `type(final).model_validate(final)` when needed.