Skip to content

Errors and error paths

A failed validation produces issues, and each issue has a path: the chain of keys and indices from the validated value down to the problem. The error map groups messages by that path. This page shows every shape a path can take.

result.errors is a Map<String, List<String>>. Keys are error paths with segments joined by dots. Nested schemas do not produce nested maps; address.city is one key. Fields that passed do not appear.

The schema below has a nested schema, a list of schemas, a map with key and value validators, and an object-level rule:

final order = l.schema({
'id': l.string().uuid().required(),
'items': l.list(l.schema({
'sku': l.string().required(),
'qty': l.int().min(1).required(),
}).required()).required(),
'address': l.schema({'city': l.string().required()}).required(),
'scores': l.map(
keyValidator: l.string().email().required(),
valueValidator: l.int().required(),
),
}).custom((data) => (data['items']! as List).isNotEmpty,
message: 'order must have at least one item');
final result = order.validate({
'id': 'nope',
'items': [
{'sku': 'A1', 'qty': 1},
{'sku': 'B2', 'qty': 0},
{'qty': 'two'},
],
'address': {},
'scores': {'not-an-email': 'x', '[email protected]': 7},
});
result.errors
{
"id": [
"id must be a valid uuid"
],
"items.1.qty": [
"qty must be greater than or equal to 1"
],
"items.2.sku": [
"sku is required"
],
"items.2.qty": [
"qty must be an integer"
],
"address.city": [
"city is required"
],
"scores": [
"scores has an invalid key \"not-an-email\""
],
"scores.not-an-email": [
"scores must be an integer"
]
}
Segment Added by Example
A field key A schema field address.city
A list index A list element items.1.qty
A map key A map value scores.not-an-email
Nothing The validated value itself, and object-level errors ''

The same issues, as ValidationIssue objects, show the path on each one:

result.issues
ValidationIssue(code: invalidUuid, path: id, message: id must be a valid uuid)
ValidationIssue(code: tooSmall, path: items.1.qty, message: qty must be greater than or equal to 1)
ValidationIssue(code: required, path: items.2.sku, message: sku is required)
ValidationIssue(code: invalidType, path: items.2.qty, message: qty must be an integer)
ValidationIssue(code: required, path: address.city, message: city is required)
ValidationIssue(code: invalidKey, path: scores, message: scores has an invalid key "not-an-email")
ValidationIssue(code: invalidType, path: scores.not-an-email, message: scores must be an integer)

Map keys are the exception to “the path names the bad value”: a key that fails its key validator is reported at the map’s own path as an invalidKey issue, because the key is the map’s problem, not a value’s. The failing value under that key is still reported at the key’s path.

getError(path) returns the first message at exactly that path, or null. getErrors(path) returns all of them. Neither throws, and neither looks into children: getErrors('items.2') is empty because the issues are at items.2.sku and items.2.qty.

result.getError('items.1.qty');
result.getErrors('items.2');
result.getError('scores');
result.getError('scores.not-an-email');
result.getError('address.city');
result.getError('address');
qty must be greater than or equal to 1
[]
scores has an invalid key "not-an-email"
scores must be an integer
city is required
null

A .custom() on a schema runs after every field passed and reports at the schema’s own path. At the root that is the empty string, so it never collides with a field named anything. In a nested schema it is the nested field’s key.

final emptyOrder = order.validate({
'id': '123e4567-e89b-12d3-a456-426614174000',
'items': [],
'address': {'city': 'Paris'},
});
emptyOrder.errors;
emptyOrder.getError('');
{: [order must have at least one item]}
order must have at least one item

For a model, the generated ErrorKeys record holds every error path as a constant, including nested ones: UserErrorKeys.address.postcode is "address.postcode", and UserErrorKeys.address.$key is "address" for the nested field’s own errors. Schema keys and error keys covers the record.