Description
unevaluatedProperies
should be applied after validating all subschemas, such that ValidationErrors
from the subschemas are raised/yielded before raising errors for unevaluatedProperies
From https://json-schema.org/draft/2020-12/draft-bhutton-json-schema-01#name-unevaluatedproperties:
"properties", "patternProperties", "additionalProperties", and all in-place applicators MUST be evaluated before this keyword can be evaluated.
The reference to "in-place applicators" includes logical keywords like allOf
and conditional keywords like if
and then
.
From this documentation, it proceeds that:
- Properties from valid subschemas (recursively) are considered "evaluated"
- Properties from invald subschemas are evaluated first, raising/yielding any ValidationErrors, before being considered "unevaluated".
This makes a significant difference when failing on the first error encountered, where the "unevaluated properties are not allowed" message is misleading for a property that is present in the schema but fails validation - especially when the validation error is nested deeply within the "unevaluated" property.
Environment
$ pip list
Package Version
------------------------- --------
attrs 25.3.0
jsonschema 4.24.0
jsonschema-specifications 2025.4.1
pip 25.1.1
referencing 0.36.2
rpds-py 0.25.1
setuptools 80.3.1
typing_extensions 4.14.0
wheel 0.45.1
Example
The (simplified) example schema attempts to define a generic "signal" schema, which may have different properties depending on it's "data type". This is arguably a bad idea but, good or bad, is possible in draft 2020-12:
{
"$id": "signal.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"unevaluatedProperties": false,
"required": ["source","destination","data type"],
"properties": {
"data type": {"enum": ["float","string"]},
"source": true,
"destination": true,
},
"allOf": [
{
"if": {"properties": {"data type": {"const": "float"}}},
"then": {"properties": {"precision": {"type": "number"}}}
},
{
"if": {"properties": {"data type": {"const": "string"}}},
"then": {"properties": {"encoding": {"enum": ["ascii", "utf-8"]}}}
}
]
}
This works as expected for well-formed objects:
validator = jsonschema.Draft202012Validator(schema)
validator.validate({"source": "A/C", "destination": "Store", "data type": "string", "encoding": "utf-8"})
validator.validate({"source": "A/C", "destination": "Store", "data type": "float", "precision": 64})
When a sub-schema gives unexpected results, the unevaluatedProperties
error is wrongly printed, instead
validator.validate({"source": "A/C", "destination": "Store", "data type": "string", "encoding": "utf-16"})
ValidationError: Unevaluated properties are not allowed ('encoding' was unexpected)
Failed validating 'unevaluatedProperties' in schema:
{'$id': 'signal.json',
'$schema': 'https://json-schema.org/draft/2020-12/schema',
'unevaluatedProperties': False,
'required': ['source', 'destination', 'data type'],
'properties': {'data type': {'enum': ['float', 'string']},
'source': True,
'destination': True},
'allOf': [{'if': {'properties': {'data type': {'const': 'float'}}},
'then': {'properties': {'precision': {'type': 'number'}}}},
{'if': {'properties': {'data type': {'const': 'string'}}},
'then': {'properties': {'encoding': {'enum': ['ascii',
'utf-8']}}}}]}
On instance:
{'source': 'A/C',
'destination': 'Store',
'data type': 'string',
'encoding': 'utf-16'}
This is not incorrect behavior, and a somewhat misleading message. The real problem is that utf-16
is not an allowed value, not that encoding
is unevaluated. Only when we print all errors, we find the real (or at least original) error:
import traceback
for error in validator.iter_errors({"source": "A/C", "destination": "Store", "data type": "string", "encoding": "utf-16"}):
traceback.print_exception(error)
jsonschema.exceptions.ValidationError: Unevaluated properties are not allowed ('encoding' was unexpected)
Failed validating 'unevaluatedProperties' in schema:
{'$id': 'signal.json',
'$schema': 'https://json-schema.org/draft/2020-12/schema',
'unevaluatedProperties': False,
'required': ['source', 'destination', 'data type'],
'properties': {'data type': {'enum': ['float', 'string']},
'source': True,
'destination': True},
'allOf': [{'if': {'properties': {'data type': {'const': 'float'}}},
'then': {'properties': {'precision': {'type': 'number'}}}},
{'if': {'properties': {'data type': {'const': 'string'}}},
'then': {'properties': {'encoding': {'enum': ['ascii',
'utf-8']}}}}]}
On instance:
{'source': 'A/C',
'destination': 'Store',
'data type': 'string',
'encoding': 'utf-16'}
jsonschema.exceptions.ValidationError: 'utf-16' is not one of ['ascii', 'utf-8']
Failed validating 'enum' in schema['allOf'][1]['then']['properties']['encoding']:
{'enum': ['ascii', 'utf-8']}
On instance['encoding']:
'utf-16'