This guide covers error handling contracts, validation reports, and practical operating patterns.
Error Types
Exdantic.Error
Every validation failure is represented as:
path([atom() | String.t() | integer()])code(atom())message(String.t())
Create errors:
error = Exdantic.Error.new([:user, :email], :format, "invalid email")Format errors:
Exdantic.Error.format(error)
# "user.email: invalid email"Exdantic.ValidationError
validate!/1 style APIs raise Exdantic.ValidationError containing the collected errors list.
Common Error Codes
Codes vary by operation, but common categories include:
:required:type:additional_properties- constraint codes like
:min_length,:gteq,:choices :model_validation:computed_field:computed_field_type- env-related settings codes like
:env_cast,:env_json,:env_key_conflict
Validation Entry Points
Compile-time schema:
MySchema.validate/1MySchema.validate!/1MySchema.validate_enhanced/2
Runtime:
Exdantic.Runtime.validate/3Exdantic.Runtime.validate_enhanced/3Exdantic.Runtime.Validator.validate/3
Universal:
Type-only:
Reporting APIs
Quick report
report = Exdantic.EnhancedValidator.validation_report(target, input)Returns a lightweight summary: validation result, generated JSON Schema, target and input analysis, timing metrics, and config summary.
Comprehensive report
report =
Exdantic.EnhancedValidator.comprehensive_validation_report(
target,
input,
test_providers: [:openai, :anthropic],
include_performance_analysis: true,
include_dspy_analysis: true
)Adds provider compatibility, complexity metrics, and recommendations.
Schema Introspection in Production
Compile-time schema helpers:
__schema_info__/0__enhanced_schema_info__/0
Useful for diagnostics and contract inventory at runtime.
Operational Patterns
1. Strict contracts at boundaries
At API or queue boundaries, prefer strict mode to catch unknown keys early.
2. Coercion strategy by trust level
- trusted/internal paths: can use
:safecoercion - external/untrusted paths: prefer strict, explicit typing
3. Separate input/output schemas when needed
If computed fields should not be accepted from input, generate output schema normally but derive input schemas with computed fields removed:
output_schema = Exdantic.JsonSchema.from_schema(MySchema)
input_schema = Exdantic.JsonSchema.remove_computed_fields(output_schema)4. Validate provider compatibility in CI
Use enhanced resolver compatibility checks before shipping schema changes:
:ok = Exdantic.JsonSchema.EnhancedResolver.validate_schema_compatibility(MySchema)5. Keep examples executable
Run example scripts as contract tests for documentation and onboarding quality.
Testing Suggestions
- Unit-test custom validators and computed field functions directly
- Add integration tests for full schema pipelines (
validate+json_schema) - Include strict-mode tests for unknown key handling
- For settings schemas, test env decoding and nested exploded env paths
- For LLM contracts, test provider-shaped schema snapshots
Documentation and Maintenance Workflow
When adding a new schema capability:
- Add/adjust tests in
test/ - Update affected guide(s)
- Update README entry points if API visibility changes
- Validate docs build (
mix docs) and examples as needed
Final Note
Exdantic's strength is consistency across compile-time, runtime, and type-only validation. Treat schemas as first-class contracts, and use the reporting and resolver tools to keep those contracts stable under change.