Skip to content

Validation Layers⚓︎

Not every table problem belongs to the same layer. Understanding the layers makes errors easier to explain and schemas easier to design.

Consider this table:

Several different problems
Given the users exist
  | name | age | email           | manager |
  | Mira | old | mira@example.io |         |
  |      | 29  | mira@example.io | unknown |

It has more than one kind of issue. Each issue should be reported at the layer that understands it best.

Field parsing⚓︎

Field parsing handles one cell becoming one Python value.

A field parsing problem
Field parser failed: invalid literal for int() with base 10: 'old'
(code=parser_failed, field='age', row=2, column=2, value='old')

This is the right layer for numbers, booleans, choices, dates, lists, and other cell-level syntax.

Required fields⚓︎

Required-field validation answers a simpler question: did the author provide the value the table needs?

A required value problem
Required field has an empty value
(code=empty_required, field='name', row=3, column=1, value='')

This is not a parsing problem. There is no value to parse.

Whole-table rules⚓︎

Some rules require more than one record. Duplicate emails are a table-level concern because no single row can know whether another row used the same email.

A table-level problem
Emails must be unique
(code=table_validation_failed, schema=UserTable)

References⚓︎

References are another layer. A cell can parse correctly and still point to an item that does not exist.

A reference problem
Reference target 'unknown' was not found
(code=reference_failed, field='manager', row=3, column=4, value='unknown')

The guides show these layers in practice: record validation, whole-table validation, and reference resolution.

Layered errors are easier to fix

If the age is invalid, point to the age cell. If emails are duplicated, explain the table rule. If a reference is missing, point to the reference cell. The reader should not have to guess which kind of mistake they made.