Errors and validation¶
Three levels of validation¶
| Level | When | Reads rows? |
|---|---|---|
| Local invariants | At construction, inside a factory | No |
| Structural rules | validate(), and before rendering |
No |
| Data validation | Only when you explicitly ask | Yes |
Construction-time checks catch a bad width or an empty title immediately, at the call site where you made the mistake. Structural checks catch cross-node problems — unknown column references, duplicate ids, overlapping blocks — before any source is touched.
from caxton import ValidationError, validate
try:
validate(document)
except ValidationError as error:
for issue in error.issues:
print(issue.code, issue.path, issue.message)
ValidationError aggregates multiple problems. Each Issue carries a message,
a semantic path, a code and structured context, so failures can be
inspected programmatically rather than string-matched.
The exception hierarchy¶
Everything inherits from CaxtonError, which carries message, path and
context, and preserves the original cause through exception chaining.
CaxtonError
├── CaxtonTypeError (also a TypeError)
├── CaxtonValueError (also a ValueError)
├── InvalidOperationError
├── UnsupportedFeatureError
├── ValidationError
│ └── SchemaError
│ ├── ColumnNotFoundError
│ └── DuplicateColumnError
├── DataSourceError
│ ├── UnsupportedDataSourceError
│ ├── DataSourceConsumedError
│ └── DataSourceIterationError
├── DataEvaluationError
│ ├── FieldAccessError
│ ├── MissingFieldError
│ ├── SourceEvaluationError
│ ├── AggregateEvaluationError
│ ├── CyclicColumnError
│ ├── GroupingError
│ └── MatrixConflictError
└── RenderError
├── BackendError
└── TemplateError
├── TemplateFormatError
└── TemplateRefError
├── MissingTemplateRefError
├── AmbiguousTemplateRefError
├── IncompatibleTemplateRefError
└── InvalidTemplateRefError
Because CaxtonTypeError and CaxtonValueError also subclass the Python
built-ins, existing except TypeError / except ValueError handlers keep
working, and you can still catch everything with except CaxtonError.
Errors worth knowing¶
| Error | Usually means |
|---|---|
DataSourceConsumedError |
A one-shot source was asked for a second pass. |
DataSourceIterationError |
Fetching the next row failed; keeps the next row index and the cause. |
MissingFieldError |
The declared field is absent from the row. |
FieldAccessError |
An existing property or descriptor raised while being read. |
CyclicColumnError |
ref() chains form a cycle. |
MatrixConflictError |
Several source values land in one matrix cell without an aggregate. |
UnsupportedFeatureError |
The selected target cannot represent the request — including an implicit block after a table of unknown height. |
TemplateRefError |
A named template target is missing, ambiguous or of the wrong shape. |
An error while retrieving the next row is a DataSourceIterationError, not a
backend failure — an important distinction when a database cursor dies mid-write.
Likewise, an error raised by an existing property never appears as a
"missing field" error.
Warnings¶
Non-fatal issues are reported through warning categories rather than exceptions:
CaxtonWarning— the base category;DocumentWarning— a document-level concern;PerformanceWarning— a choice that will be slow or memory-hungry;ExperimentalFeatureWarning— behaviour that may still change.
import warnings
from caxton import render
from caxton.core.errors import PerformanceWarning
with warnings.catch_warnings():
warnings.simplefilter("error", category=PerformanceWarning)
render(document)
The warning categories live in caxton.core.errors; the exception classes are
also re-exported from the short caxton facade.
Failure timing¶
Caxton resolves requirements, workbook operation, capabilities and renderer compatibility before opening or writing the target. A capability or template failure therefore happens while the destination is still untouched, and a failed path write leaves the previous file intact.