Skip to content

Expo Errors and Metadata

SteffenBlake edited this page Sep 28, 2026 · 1 revision

Errors and metadata

Expo returns ErrorDetail values for failed rules. Each contains a human-readable Detail and a JSON Pointer in Pointer. Generated metadata describes the model's rules without running validation.

Consume validation failures

var registration = new CourseRegistration
{
    StudentEmail = "invalid",
    CourseCode = "B"
};

if (!registration.TryValidate(out var errors))
{
    foreach (var error in errors)
    {
        Console.WriteLine($"{error.Pointer}: {error.Detail}");
    }
}

CourseRegistration is defined in Validation models. Expo aggregates failures instead of stopping at the first invalid property. Several rules may report the same pointer, so preserve a collection of messages when grouping errors by field.

Property paths

Pointer Invalid value
/StudentEmail A scalar property
/CourseIds/2 The third collection item
/Lead/Email A nested child's property
/Guests/0/Email A property on the first child in a collection

Pointers use declared C# property names. A JSON naming policy or [JsonPropertyName] can give the serialized property a different name; validation pointers still use the C# name. JSON Pointer escaping replaces ~ with ~0 and / with ~1 in path segments.

Customize messages

Most built-in rules take an optional message as their last argument:

[StringHasMinimumLength(2, "A course code needs at least two characters.")]
public string? CourseCode { get; init; }

Generated comparison and regex attributes also accept an optional message. Reusable custom attributes can expose a message, while model-specific custom methods return their failure strings directly.

Expo supplies a default message when none is specified. Choose a custom message when your users need a domain term or a useful correction.

Inspect generated metadata

Each Expo model exposes static ExpoModelMetadata Metadata:

foreach (var property in CourseRegistration.Metadata.Properties)
{
    Console.WriteLine($"{property.Name}: {property.Pointer}");
    foreach (var rule in property.Rules)
    {
        Console.WriteLine($"{rule.Kind}: {rule.Message}");
    }
}

Each property entry contains its name, escaped pointer, ordered rules, and IsNestedValidatable flag. Rule entries can include a constant value, compared property name, message, custom rule name, regex pattern, or format, depending on the rule kind.

Metadata describes constraints rather than a validation outcome. A custom rule's metadata identifies the rule; call TryValidate to evaluate its code against an actual model. Nested models expose their own metadata.

For HTTP failure responses, see Expo validation in ASP.NET Core. For publishing constraints in API schemas, see Expo OpenAPI.

Clone this wiki locally