From 0e525da26388af1d1046619ccc26318be73e0f32 Mon Sep 17 00:00:00 2001 From: Vishal Bala Date: Fri, 30 Oct 2026 15:07:33 +0100 Subject: [PATCH] fix(schema): enforce INT8/UINT8 range check on list vectors The vector validator compared `str(datatype).upper()` against "INT8" and "UINT8", but `str()` on a str-mixin Enum member returns "VectorDataType.INT8", so neither branch ever matched and out-of-range values (e.g. [999, -999] for an int8 field) passed validation on every Python version. Compare enum members directly, as `_map_field_to_pydantic_type` already does. Behaviour change: validate_object (and load with validate_on_load=True) now rejects JSON int8 vectors outside -128..127 and uint8 vectors outside 0..255, which it previously accepted. --- redisvl/schema/validation.py | 28 ++++++++---------- tests/unit/test_validation.py | 53 +++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 16 deletions(-) diff --git a/redisvl/schema/validation.py b/redisvl/schema/validation.py index 3bd7cf9e..d629129c 100644 --- a/redisvl/schema/validation.py +++ b/redisvl/schema/validation.py @@ -174,22 +174,18 @@ def _validate_vector(cls, value): raise ValueError( f"Vector field '{fname}' must have {dims} dimensions, got {len(value)}" ) - # Validate data types - datatype_str = str(datatype).upper() - # Integer-based datatypes - if datatype_str in ("INT8", "UINT8"): - # Check range for INT8 - if datatype_str == "INT8": - if any(v < -128 or v > 127 for v in value): - raise ValueError( - f"Vector field '{fname}' contains values outside the INT8 range (-128 to 127)" - ) - # Check range for UINT8 - elif datatype_str == "UINT8": - if any(v < 0 or v > 255 for v in value): - raise ValueError( - f"Vector field '{fname}' contains values outside the UINT8 range (0 to 255)" - ) + # Check range for INT8 + if datatype == VectorDataType.INT8: + if any(v < -128 or v > 127 for v in value): + raise ValueError( + f"Vector field '{fname}' contains values outside the INT8 range (-128 to 127)" + ) + # Check range for UINT8 + elif datatype == VectorDataType.UINT8: + if any(v < 0 or v > 255 for v in value): + raise ValueError( + f"Vector field '{fname}' contains values outside the UINT8 range (0 to 255)" + ) return value return _validate_vector diff --git a/tests/unit/test_validation.py b/tests/unit/test_validation.py index f34d0035..fa12ba81 100644 --- a/tests/unit/test_validation.py +++ b/tests/unit/test_validation.py @@ -529,6 +529,59 @@ def test_vector_field_validation( ) +class TestIntegerVectorRangeValidation: + """Range checks for INT8 and UINT8 vectors given as lists (JSON storage).""" + + @pytest.fixture + def int_vector_schema(self): + return IndexSchema.from_dict( + { + "index": {"name": "int-vectors", "storage_type": "json"}, + "fields": [ + { + "name": field_name, + "type": "vector", + "attrs": { + "algorithm": "flat", + "dims": 2, + "datatype": datatype, + "distance_metric": "l2", + }, + } + for field_name, datatype in (("i8", "int8"), ("u8", "uint8")) + ], + } + ) + + @pytest.mark.parametrize( + "field_name,value", + [ + ("i8", [-128, 127]), + ("u8", [0, 255]), + ], + ) + def test_in_range_values_accepted(self, int_vector_schema, field_name, value): + assert validate_object(int_vector_schema, {field_name: value}) == { + field_name: value + } + + @pytest.mark.parametrize( + "field_name,value,error_text", + [ + ("i8", [999, -999], "INT8 range"), + ("i8", [-129, 0], "INT8 range"), + ("i8", [0, 128], "INT8 range"), + ("u8", [-1, 0], "UINT8 range"), + ("u8", [0, 256], "UINT8 range"), + ], + ) + def test_out_of_range_values_rejected( + self, int_vector_schema, field_name, value, error_text + ): + with pytest.raises(ValueError, match=error_text): + validate_object(int_vector_schema, {field_name: value}) + + class TestNestedJsonValidation: """Tests for JSON path-based validation with nested structures."""