From e1f9eb65d5f9c0b9f855710a0a958ffa1f8ad535 Mon Sep 17 00:00:00 2001 From: osc-bot Date: Mon, 23 Mar 2026 22:35:48 +0800 Subject: [PATCH] fix(dynamodb): reject empty sets and non-string map keys in TypeSerializer - _is_type_set now checks len(value) > 0 to prevent vacuous truth matching empty sets, which produce invalid {'NS': []} that DynamoDB rejects (fixes #4738) - _serialize_m now validates all map keys are strings before serialization, raising TypeError for non-string keys that would produce confusing errors later (fixes #4739) Both fixes raise TypeError with clear messages at serialization time rather than letting invalid data reach the DynamoDB API. --- boto3/dynamodb/types.py | 7 ++++++- tests/unit/dynamodb/test_types.py | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/boto3/dynamodb/types.py b/boto3/dynamodb/types.py index f358b12f55..17a0cd46bd 100644 --- a/boto3/dynamodb/types.py +++ b/boto3/dynamodb/types.py @@ -189,7 +189,7 @@ def _is_set(self, value): return False def _is_type_set(self, value, type_validator): - if self._is_set(value): + if self._is_set(value) and len(value) > 0: if False not in map(type_validator, value): return True return False @@ -237,6 +237,11 @@ def _serialize_l(self, value): return [self.serialize(v) for v in value] def _serialize_m(self, value): + for k in value: + if not isinstance(k, str): + raise TypeError( + f'Map keys must be strings, got {type(k).__name__}: {k!r}' + ) return {k: self.serialize(v) for k, v in value.items()} diff --git a/tests/unit/dynamodb/test_types.py b/tests/unit/dynamodb/test_types.py index 0a6c2a07f3..680a78f01b 100644 --- a/tests/unit/dynamodb/test_types.py +++ b/tests/unit/dynamodb/test_types.py @@ -143,6 +143,14 @@ def test_serialize_map(self): 'M': {'foo': {'S': 'bar'}, 'baz': {'M': {'biz': {'N': '1'}}}} } + def test_serialize_empty_set_raises_error(self): + with pytest.raises(TypeError, match=r'Unsupported type'): + self.serializer.serialize(set()) + + def test_serialize_map_with_non_string_key_raises_error(self): + with pytest.raises(TypeError, match=r'Map keys must be strings'): + self.serializer.serialize({1: 'a'}) + class TestDeserializer(unittest.TestCase): def setUp(self):