Refactor SchemaValidationFailure Struct Fields for Clarity
tl;dr - I'm working with @its-hammer-time to the following end goal:
Provide full, consistent information in
SchemaValidationFailurewhen an OpenAPI Schema fails validation whether it originates from the underlyingjsonschemalibrary orlibopenapi-validator. This will allow clients usinglibopenapi-validatorto offer full visibility into what aspects of an input object failed validation to THEIR clients.
In order to do this, I propose some changes to struct fields on SchemaValidationFailure. Then I will make changes so that each existing validation is consistent with those definitions.
Once that's done, I'd like to merge this into a working branch as the changes will be breaking. Then, I'd like to implement the changes in error mapping/handling in another branch of my fork (and create another PR).
Full details below:
SchemaValidationFailure Struct Refactoring
tl;dr I wanted to get full clarity on how things will change before making all the changes to re-map fields, locations, values, etc.
This PR clarifies the purpose of each struct field on SchemaValidationFailure, and get buy in on that before making a huge batch of changes.
Summary
Refactored SchemaValidationFailure struct to align with JSON Schema specification terminology, improve clarity, and eliminate ambiguity in field naming.
Changes Made
1. Renamed Fields for JSON Schema Alignment
DeepLocation → KeywordLocation
-
Before:
DeepLocation string- Ambiguous name, unclear what "deep" referred to -
After:
KeywordLocation string- Matches JSON Schema spec terminology -
Rationale:
-
KeywordLocationis the official JSON Schema term from thejsonschema/v6library - Refers to the path in the schema to the violated keyword (e.g.,
/properties/email/pattern) - Makes it clear this is about the schema rule, not the data
- Consistent with
er.KeywordLocationfromjsonschema.OutputUnit
-
AbsoluteLocation → AbsoluteKeywordLocation
-
Before:
AbsoluteLocation string- Lost the "Keyword" context -
After:
AbsoluteKeywordLocation string- Full, unambiguous name -
Rationale:
- Matches JSON Schema spec terminology exactly
- Pairs naturally with
KeywordLocation(relative vs absolute) - Consistent with
er.AbsoluteKeywordLocationfromjsonschema.OutputUnit - Makes it clear this is also a schema location, just with absolute URI
2. Field Organization Improvements
Moved to Better Logical Grouping
// Data instance location (where in the data being validated):
InstancePath []string // Raw path segments: ["user", "email"]
FieldName string // Last segment: "email"
FieldPath string // JSONPath format: "$.user.email"
// Schema location (where in the schema that failed):
KeywordLocation string // Relative: "/properties/email/pattern"
AbsoluteKeywordLocation string // Absolute: "https://..."
This separation makes it crystal clear:
-
InstancePath,FieldPath,FieldName→ Where in the DATA -
KeywordLocation,AbsoluteKeywordLocation→ Where in the SCHEMA
3. Removed Unused Field ReferenceExample
ReferenceExample - DELETED
-
Rationale:
- Grep search found zero usages across entire codebase
- Field was never populated or consumed
- Removing reduces struct size and complexity
4. Improved Documentation
ReferenceSchema Comment
- Before: "The schema that was referenced in the validation failure"
- After: "The schema that was referenced in the validation failure"
- Still clear, accurate description
ReferenceObject Comment
- Before: "The object that was referenced in the validation failure"
- After: "The object that failed schema validation"
- Rationale: More specific - this is the actual failed data, not just "referenced"
5. Prepared Location for deletion
Location - DEPRECATED
- Status: Moved to bottom of struct, marked as deprecated
-
Deprecation Comment:
// DEPRECATED in favor of explicit use of FieldPath & InstancePath -
Rationale:
- Was inconsistently set throughout codebase:
- Sometimes
er.InstanceLocation(data location) - Sometimes
er.KeywordLocation(schema location) - Sometimes static strings like
"unavailable","schema compilation","/required"
- Sometimes
- This ambiguity made it unreliable for consumers
- Now have explicit fields: use
FieldPath/InstancePathfor data,KeywordLocationfor schema
- Was inconsistently set throughout codebase:
- This will be deleted in a followup PR when all usages are by struct fields pertaining to the schema or keyword.(whichever is appropriate)
6. Fixed Comment Typo
Line 99 in ValidationError struct
- Before: "This is only populated whe the validation type is against a schema"
- After: "This is only populated when the validation type is against a schema"
Verification
Compilation Check
- ✅ All usages of
DeepLocation→KeywordLocationupdated - ✅ All usages of
AbsoluteLocation→AbsoluteKeywordLocationupdated - ✅ No remaining references to old field names
- ✅ All tests pass
Updated Files
-
errors/validation_error.go- Struct definitions -
schema_validation/validate_schema.go- SetsKeywordLocationandAbsoluteKeywordLocation -
schema_validation/validate_document.go- SetsKeywordLocationandAbsoluteKeywordLocation
Backward Compatibility
- ✅
Locationfield still exists (deprecated but functional) - ✅ JSON/YAML tags unchanged for non-renamed fields
- ✅ New field names have appropriate JSON tags (
keywordLocation,absoluteKeywordLocation)
ValidationType/SubType will tell us what part of the HTTP request failed
ValidationError already provides clear HTTP component context via:
-
ValidationType: "parameter", "requestBody", "response" -
ValidationSubType: "path", "query", "header", "cookie", "schema"
This means consumers can easily determine if an error is from:
- Path parameters:
ValidationType == "parameter" && ValidationSubType == "path" - Query parameters:
ValidationType == "parameter" && ValidationSubType == "query" - Headers:
ValidationType == "parameter" && ValidationSubType == "header" - Cookies:
ValidationType == "parameter" && ValidationSubType == "cookie" - Request body:
ValidationType == "requestBody" - Response body:
ValidationType == "response"
The SchemaValidationFailure.Location field was never meant to indicate HTTP component - it was for within-field location.
Future Work (Separate Commit)
1. Add Tests for KeywordLocation Invariant
After the schema refactoring is complete, add tests to validate the following invariant:
- When
OriginalJsonSchemaErrorisnil, bothKeywordLocationandAbsoluteKeywordLocationshould be empty strings - When
OriginalJsonSchemaErroris notnil, both fields may be populated (when the error originates from JSON Schema validation)
Rationale: This documents the expected behavior that keyword location fields are only relevant when the validation failure originated from JSON Schema validation, not from other types of validation (e.g., parameter encoding errors, schema compilation errors).
Test scenarios:
func TestSchemaValidationFailure_KeywordLocations_WhenNotFromJsonSchema(t *testing.T) {
// When OriginalJsonSchemaError is nil, KeywordLocation fields should be empty
}
func TestSchemaValidationFailure_KeywordLocations_WhenFromJsonSchema(t *testing.T) {
// When OriginalJsonSchemaError is set, KeywordLocation fields may be populated
}
2. Remove Deprecated Location Field
Completely remove the deprecated Location field from SchemaValidationFailure in favor of its more specific counterparts:
- Use
FieldNamefor the specific field that failed - Use
FieldPathfor the JSONPath representation
Current state:
// DEPRECATED in favor of explicit use of FieldPath & InstancePath
Location string `json:"location,omitempty" yaml:"location,omitempty"`
Actions required:
- Remove the
Locationfield from the struct - Update all code that sets
Locationto useFieldPathorFieldNameinstead - Update the
Error()method to use non-deprecated fields
3. Update SchemaValidationFailure.Error() Method
The Error() method currently uses the deprecated Location field:
// Current (uses deprecated field):
func (s *SchemaValidationFailure) Error() string {
return fmt.Sprintf("Reason: %s, Location: %s", s.Reason, s.Location)
}
Should be updated to use non-deprecated fields:
// Proposed:
func (s *SchemaValidationFailure) Error() string {
if s.FieldPath != "" && s.KeywordLocation != "" {
return fmt.Sprintf("Reason: %s, Field: %s, Keyword: %s",
s.Reason, s.FieldPath, s.KeywordLocation)
}
if s.FieldPath != "" {
return fmt.Sprintf("Reason: %s, Field: %s", s.Reason, s.FieldPath)
}
if s.KeywordLocation != "" {
return fmt.Sprintf("Reason: %s, Keyword: %s", s.Reason, s.KeywordLocation)
}
return fmt.Sprintf("Reason: %s", s.Reason)
}
Note: This change should be done in conjunction with removing the Location field to ensure all error messages remain informative.
Benefits
- Alignment with Standards: Now uses official JSON Schema specification terminology
- Clarity: Clear separation between data locations and schema locations
-
Consistency: Field names match the source (
jsonschema.OutputUnit) -
Reduced Ambiguity: Deprecated the confusing
Locationfield - Better Documentation: Improved comments make purpose clear
-
Cleaner Code: Removed unused
ReferenceExamplefield
Breaking Changes
⚠️ This is a breaking change for consumers who use:
-
DeepLocationfield (nowKeywordLocation) -
AbsoluteLocationfield (nowAbsoluteKeywordLocation)
However, this is justified because:
- These names were misleading and caused confusion
- New names align with industry standard terminology
- The change makes the API more intuitive and self-documenting
- Consumers should be migrating away from
Locationanyway (now deprecated)
Related Context
Ongoing Work: Path Parameter Validation Errors
This refactoring is part of a larger effort to ensure all validation errors include comprehensive SchemaValidationErrors. We're systematically reviewing all error paths in ValidatePathParamsWithPathItem to populate schema validation details consistently.
Codecov Report
:white_check_mark: All modified and coverable lines are covered by tests.
:white_check_mark: Project coverage is 97.34%. Comparing base (38ab1b5) to head (3696bc8).
Additional details and impacted files
@@ Coverage Diff @@
## main #188 +/- ##
=======================================
Coverage 97.34% 97.34%
=======================================
Files 42 42
Lines 3761 3761
=======================================
Hits 3661 3661
Misses 100 100
| Flag | Coverage Δ | |
|---|---|---|
| unittests | 97.34% <100.00%> (ø) |
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.