From 825e69ca5c6bbdbc93f87a1f1b583f3d47114eb9 Mon Sep 17 00:00:00 2001 From: Kailash Nelson <37966146+KingLizard1020@users.noreply.github.com> Date: Thu, 17 Sep 2026 17:34:37 +0000 Subject: [PATCH] Raise ValueError for naive datetime search attributes Mirror the untyped encoder timezone check into the typed path so upserting or encoding a datetime search attribute without tzinfo fails client-side instead of as a server BadSearchAttributes error. Signed-off-by: Kailash Nelson <37966146+KingLizard1020@users.noreply.github.com> --- CHANGELOG.md | 4 ++++ temporalio/converter/_search_attributes.py | 2 ++ tests/test_converter.py | 13 +++++++++++++ 3 files changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f016f34ff..6cc0d03fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,10 @@ to include examples, links to docs, or any other relevant information. ### Fixed +- Encoding a datetime search attribute without a timezone now raises + `ValueError("Timezone must be present on all search attribute dates")` on + the typed path, matching the deprecated untyped encoder, instead of sending + a naive ISO string that the server rejects with `BadSearchAttributes`. - Current workflow and activity payload converter accessors now return the configured converter without SDK-internal transfer type conversion. diff --git a/temporalio/converter/_search_attributes.py b/temporalio/converter/_search_attributes.py index 4ec154d6f..4a8191bbd 100644 --- a/temporalio/converter/_search_attributes.py +++ b/temporalio/converter/_search_attributes.py @@ -61,6 +61,8 @@ def encode_typed_search_attribute_value( ) # datetime needs to be in isoformat if isinstance(value, datetime): + if value.tzinfo is None: + raise ValueError("Timezone must be present on all search attribute dates") value = value.isoformat() # We'll do an extra sanity check for keyword list and check every value if isinstance(value, Sequence): diff --git a/tests/test_converter.py b/tests/test_converter.py index 499152096..d167100dc 100644 --- a/tests/test_converter.py +++ b/tests/test_converter.py @@ -52,6 +52,7 @@ create_payload_validation_error, decode_search_attributes, encode_search_attribute_values, + encode_typed_search_attribute_value, transfer_type_convertible, value_to_type, ) @@ -467,6 +468,18 @@ def test_encode_search_attribute_values(): encode_search_attribute_values(["foo", 123]) # type: ignore[arg-type] +def test_encode_typed_search_attribute_value_datetime_requires_timezone(): + key = temporalio.common.SearchAttributeKey.for_datetime("checkout_time") + with pytest.raises(ValueError, match="Timezone must be present"): + encode_typed_search_attribute_value( + key, datetime(2024, 7, 5, 15, 43, 7, 875302) + ) + payload = encode_typed_search_attribute_value( + key, datetime(2024, 7, 5, 15, 43, 7, 875302, tzinfo=timezone.utc) + ) + assert payload.metadata["type"] == b"Datetime" + + def test_decode_search_attributes(): """Tests decode from protobuf for python types"""