python-jsonschema / python-jsonschema/jsonschema
Incorrect validation of valid payload with valid schema
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 5k
- Forks
- 671
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 10
Description
I have a schema that heavily utilises $dynamicRef to inject parts of schema from other files.
Namely, here is the base schema:
base.json:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "base.json",
"type": "object",
"additionalProperties": false,
"properties": {
"orderNumber": {
"type": "integer",
"minimum": 0
},
"downstreamNumber": {
"$dynamicRef": "#specialDownstreamNumber"
},
"schemaName": {
"$dynamicRef": "#concreteName"
},
"schemaVersion": {
"$dynamicRef": "#concreteVersion"
},
"timestamp": {
"type": "integer"
},
"data": {
"$dynamicRef": "#concreteData"
}
},
"required": [
"data",
"orderNumber",
"schemaName",
"schemaVersion",
"downstreamNumber",
"timestamp"
],
"$defs": {
"name": {
"const": "base",
"$dynamicAnchor": "concreteName"
},
"version": {
"const": "0.0.0",
"$dynamicAnchor": "concreteVersion"
},
"data": {
"not": true,
"$dynamicAnchor": "concreteData"
},
"downstreamNumber": {
"type": "integer",
"minimum": 1,
"$dynamicAnchor": "specialDownstreamNumber"
}
}
}
And here is the specific JSON that fills in the data for the base:
camera.json:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "camera.json",
"$ref": "base.json",
"$defs": {
"name": {
"const": "camera.info",
"$dynamicAnchor": "concreteName"
},
"version": {
"const": "1.0.0",
"$dynamicAnchor": "concreteVersion"
},
"data": {
"$dynamicAnchor": "concreteData",
"type": "object",
"additionalProperties": false,
"properties": {
"cameraFacing": {
"enum": [
"Front",
"Back"
]
},
"cameraFrameSize": {
"$ref": "#/$defs/size"
},
"roi": {
"$ref": "#/$defs/size"
},
"viewPortAspectRatio": {
"type": "number"
}
},
"required": [
"cameraFacing",
"cameraFrameSize",
"roi",
"viewPortAspectRatio"
]
},
"downstreamNumber": {
"type": "integer",
"minimum": 0,
"$dynamicAnchor": "specialDownstreamNumber"
},
"size": {
"type": "object",
"additionalProperties": false,
"properties": {
"width": {
"type": "integer"
},
"height": {
"type": "integer"
}
},
"required": [
"width",
"height"
]
}
}
}
Here is the JSON that I'm trying to validate:
{
"orderNumber": 4,
"downstreamNumber": 1,
"schemaVersion": "1.0.0",
"schemaName": "camera.info",
"timestamp": 1756525700,
"data": {
"cameraFacing": "Back",
"cameraFrameSize": {
"width": 3841,
"height": 2161
},
"roi": {
"width": 3840,
"height": 1858
},
"viewPortAspectRatio": 2.066
}
}
Here is how I'm doing the validation:
import json
from pathlib import Path
from jsonschema.validators import Draft202012Validator
from referencing import Registry, Resource
ROOT = Path(__file__).absolute().parent
SCHEMAS = ROOT / Path("schema")
def validate_file(validator: Draft202012Validator, file: Path):
with open(file, "rt", encoding="utf-8") as f:
instance = json.load(f)
instance = json.loads(file.read_text())
print(f"Validating {file}...")
validator.validate(instance)
def retrieve_from_filesystem(uri: str):
path = SCHEMAS / Path(uri)
contents = json.loads(path.read_text())
return Resource.from_contents(contents)
def main():
registry = Registry(retrieve=retrieve_from_filesystem)
schema_file = SCHEMAS / Path("camera.json")
with open(schema_file, "rt", encoding="utf-8") as f:
schema = json.load(f)
validator = Draft202012Validator(
schema,
registry=registry
)
tests_path = ROOT / Path("payload")
for file in tests_path.iterdir():
validate_file(validator, file)
if __name__ == "__main__":
main()
The validation fails with exception:
jsonschema.exceptions._WrappedReferencingError: PointerToNowhere: '/$defs/size' does not exist within {'$schema': 'https://json-schema.org/draft/2020-12/schema', '$id': 'base.json', 'type': 'object', 'additionalProperties': False, 'properties': {'orderNumber': {'type': 'integer', 'minimum': 0}, 'downstreamNumber': {'$dynamicRef': '#specialDownstreamNumber'}, 'schemaName': {'$dynamicRef': '#concreteName'}, 'schemaVersion': {'$dynamicRef': '#concreteVersion'}, 'timestamp': {'type': 'integer'}, 'data': {'$dynamicRef': '#concreteData'}}, 'required': ['data', 'orderNumber', 'schemaName', 'schemaVersion', 'downstreamNumber', 'timestamp'], '$defs': {'name': {'const': 'base', '$dynamicAnchor': 'concreteName'}, 'version': {'const': '0.0.0', '$dynamicAnchor': 'concreteVersion'}, 'data': {'not': True, '$dynamicAnchor': 'concreteData'}, 'downstreamNumber': {'type': 'integer', 'minimum': 1, '$dynamicAnchor': 'specialDownstreamNumber'}}}
Which is incorrect, as camera.json does contain /$defs/size. I've tried validating the payload with a different validator and it validates the payload successfully.
I don't see anything wrong with my schema definitions and boon has no problem validating the payload.
Is this a bug in jsonschema library or did I do something wrong and boon fails to detect it?
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the failure with base.json, camera.json, the payload files, and the shown Draft202012Validator script. Start by tracing how the registry resolves the $dynamicRef and the relative $ref to #/$defs/size across the two schemas. Done means determining whether the exception matches JSON Schema and, if not, adding a regression test covering this schema arrangement.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend-api-design
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100