Improved Error Reporting Consistency - SchemaValidationFailures are consistently returned, and error mapping information is consistently mapped
Description
TL;DR
Review these 4 PRs in order in my fork for smaller chunks, but add discussion here so that they can live throughout the project's history:
- Pre-validation Error Cleanup
- JSON Pointer Helpers
- Parameter Schema Context
- Unify Context and Centralize
Overview
Continues on the work in https://github.com/pb33f/libopenapi-validator/pull/188 to fully improve on validation error reporting.
This PR aims to make validation error reporting fully consistent so that library users can fully answer the following questions in 4xx API responses to client requests:
- Where did validation fail?
- What failed (i.e. field/param)?
- What is the path to where validation failed (if applicable)?
- a human readable error message (already exists)
All locations are noted from a keyword or constraint location in an OpenAPI schema.
Major Changes
-
Inconsistent
SchemaValidationFailureusage: Pre-validation errors (JSON decode failures, schema compilation errors) includedSchemaValidationFailureobjects, while actual schema violations (on specific parameters etc.) sometimes lacked them. -
Incomplete location information:
KeywordLocationwas often empty or contained relative paths instead of absolute RFC 6901 JSON Pointer paths from the OpenAPI document root, making it hard to identify which part of the OpenAPI spec was violated. -
Inconsistent field population: Critical fields like
ReferenceSchema,Context, andKeywordLocationwere missing or inconsistent across different validation types. -
Redundant/unused fields:
AbsoluteKeywordLocationwas always empty due to schema inlining, and theLocationfield grew to be used in different ways over the life of the repo.
Solution
This PR establishes clear patterns for error reporting:
1. Clear Error Type Separation
-
Pre-validation errors (schema compilation, JSON decode, missing schemas) → NO
SchemaValidationFailure -
Schema constraint violations (type mismatches, enum violations, range errors) → ALWAYS include
SchemaValidationFailurewith complete context
2. Complete OpenAPI-Aware Location Information
-
KeywordLocationcontains full JSON Pointer path from OpenAPI document root to the exact schema keyword - Format:
/paths/{escaped-path}/{operation}/parameters/{name}/schema/{keyword} - Example:
/paths/~1users~1{id}/get/parameters/id/schema/minimum - RFC 6901 compliant escaping (
~→~0,/→~1)
3. Consistent Field Population
-
ReferenceSchema: Rendered schema as JSON string (for human consumption) -
Context: Raw*base.Schemaobject (for programmatic access) -
KeywordLocation: Full OpenAPI path to failed schema keyword -
FieldName,FieldPath,InstancePath: Consistently populated across all validation types
4. Cleanup
-
Removed
Locationfield: Ambiguous and superseded byKeywordLocation+FieldPath -
Removed
AbsoluteKeywordLocation: Never populated due to schema inlining
Benefits
API consumers can now:
- ✅ Distinguish between pre-validation failures and actual schema violations
- ✅ Programmatically locate the exact schema keyword violated in their OpenAPI spec
- ✅ Access both human-readable (
ReferenceSchema) and programmatic (Context) schema representations - ✅ Build better error messages and debugging tools
- ✅ Implement automated spec correction or validation guidance
Breaking Changes
⚠️ Removed Field: Location
The deprecated Location field has been completely removed from SchemaValidationFailure.
Migration Guide:
- If you were using
Locationfor the schema location → useKeywordLocation - If you were using
Locationfor the instance location → useFieldPath
Example:
// Before
if err.SchemaValidationErrors[0].Location == "/properties/email/format" {
// schema location usage
}
// After
if err.SchemaValidationErrors[0].KeywordLocation == "/properties/email/format" {
// schema location usage - more explicit
}
// OR
if err.SchemaValidationErrors[0].FieldPath == "$.email" {
// instance location usage - use FieldPath instead
}
⚠️ Removed Field: AbsoluteKeywordLocation
This field was always empty because RenderInline() resolves all $ref references before validation.
Validation Types Covered
- ✅ Request body validation
- ✅ Response body validation
- ✅ Response header validation
- ✅ Path parameter validation (8 error functions)
- ✅ Query parameter validation (14 error functions)
- ✅ Header parameter validation (7 error functions)
- ✅ Cookie parameter validation (6 error functions)
- ✅ Schema/document validation
Detailed Breakdown
For easier review, this changeset has been broken down into 4 stacked PRs in my fork. Each PR focuses on a specific aspect:
-
PR 1: Pre-validation Error Cleanup
- Distinguishes pre-validation errors from schema constraint violations
- Removes
AbsoluteKeywordLocationfield - 6 commits covering schema, document, parameter, request, and response validation
-
PR 2: JSON Pointer Helpers
- Introduces centralized RFC 6901 JSON Pointer construction helpers
- Demonstrates pattern with response header validation
- 2 commits
-
PR 3: Parameter Schema Context
- Adds full OpenAPI context to all parameter errors (path, query, header, cookie)
- 4 commits (one per parameter type)
-
PR 4: Unify Context and Centralize
- Removes deprecated
Locationfield - Consolidates all JSON Pointer construction to use helpers (72+ locations)
- Unifies
Contextfield to use*base.Schemaconsistently - 2 commits
- Removes deprecated
Each PR includes:
- Detailed rationale for changes
- Tables showing before/after for each validation type
- Examples of the improvements
Testing
- ✅ All existing tests updated to reflect new error structure
- ✅ Schema compilation error tests correctly expect NO
SchemaValidationFailure - ✅ Schema constraint violation tests correctly expect complete
SchemaValidationFailure - ✅
KeywordLocationassertions updated to expect full OpenAPI paths - ✅ No linter errors
Scope of Changes (14 commits total)
The changeset touches error reporting across the entire library:
-
errors/validation_error.go- Struct definitions -
errors/parameter_errors.go- 35 parameter error functions updated -
helpers/json_pointer.go- New centralized JSON Pointer helpers -
parameters/*.go- All parameter validation (path, query, header, cookie) -
requests/validate_request.go- Request body validation -
responses/*.go- Response body and header validation -
schema_validation/*.go- Schema and document validation - All corresponding test files
Commit Phases
Phase 1: Pre-validation Error Cleanup (6 commits)
- Remove SchemaValidationFailure from schema pre-validation errors
- Remove SchemaValidationFailure from document compilation errors
- Parameters: add KeywordLocation when formatting JSON schema errors, remove SchemaValidationFailure when compilation fails
- Remove AbsoluteKeywordLocation field - never populated due to schema inlining
- Request body validation: remove SchemaValidationFailure from pre-validation errors
- Response body validation: remove SchemaValidationFailure from pre-validation errors
Phase 2: Centralized JSON Pointer Helpers (2 commits) 7. Add centralized JSON Pointer construction helpers 8. Response headers: add SchemaValidationFailure with full OpenAPI path (using helpers)
Phase 3: Parameter Schema Context (4 commits) 9. Path parameters: render schema once, pass to error functions 10. Query parameters: add full OpenAPI context + missing required param fix 11. Header parameters: add full OpenAPI context 12. Cookie parameters: add full OpenAPI context
Phase 4: Unify and Centralize (2 commits) 13. Remove deprecated Location field from SchemaValidationFailure (+ Context field unification) 14. Refactor: use centralized JSON Pointer helpers across codebase (72+ locations)
Review Notes
📋 For easier review, I recommend reviewing the stacked PRs in my fork sequentially:
- Start with PR #4 to understand the conceptual changes
- Review PR #5 to see the helper function approach
- Review PR #6 to see the pattern applied to parameters
- Review PR #7 to see the final cleanup and consolidation
Each PR builds on the previous one and can be reviewed independently for logic and correctness.
Requested blessing
-
AbsoluteKeywordLocationremoval: This field was always empty due toRenderInline()resolving refs. Is this observation correct, or are there use cases where this field should be populated?
Codecov Report
:x: Patch coverage is 99.05363% with 6 lines in your changes missing coverage. Please review.
:white_check_mark: Project coverage is 97.50%. Comparing base (bde0446) to head (d750797).
Additional details and impacted files
@@ Coverage Diff @@
## main #200 +/- ##
==========================================
+ Coverage 97.41% 97.50% +0.09%
==========================================
Files 45 46 +1
Lines 3987 4332 +345
==========================================
+ Hits 3884 4224 +340
- Misses 103 107 +4
- Partials 0 1 +1
| Flag | Coverage Δ | |
|---|---|---|
| unittests | 97.50% <99.05%> (+0.09%) |
:arrow_up: |
Flags with carried forward coverage won't be shown. Click here to find out more.
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
:rocket: New features to boost your workflow:
- :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
I am going to have to spend some time on this, it's a lot to read and grok.
I am going to have to spend some time on this, it's a lot to read and grok.
For some context, we're just trying to make it so the validation errors raised by the library follow a pattern that we can code against. At the moment, there's no ErrorCode returned that we can switch-case on so we're left with doing weird things like using a regex on the Reason field then branching logic based on that. The reason we have to do that is depending where the error came from, some fields may or may not be populated so it's a confusing experience.
What we're trying to do is catalog the errors into two groups then as long as it fits into one of those groups you'll always get the same fields hydrated.
- ValidationError (root) - Errors unrelated to the schema like missing request body, invalid response code, etc.
- SchemaValidationError - Errors related to JSONSchema (or OpenAPI schema, specifically for parameters).
I am going to have to spend some time on this, it's a lot to read and grok.
For some context, we're just trying to make it so the validation errors raised by the library follow a pattern that we can code against. At the moment, there's no
ErrorCodereturned that we can switch-case on so we're left with doing weird things like using a regex on theReasonfield then branching logic based on that. The reason we have to do that is depending where the error came from, some fields may or may not be populated so it's a confusing experience.What we're trying to do is catalog the errors into two groups then as long as it fits into one of those groups you'll always get the same fields hydrated.
- ValidationError (root) - Errors unrelated to the schema like missing request body, invalid response code, etc.
- SchemaValidationError - Errors related to JSONSchema (or OpenAPI schema, specifically for parameters).
Yes, I agree with the notion and the intent. I just have to spend some time reading this slowly. Speed is not our friend with situations like this .