From f07d7f460643280a2f584add9350a6beb469e398 Mon Sep 17 00:00:00 2001 From: Andy Staples Date: Fri, 12 Sep 2025 11:29:49 -0700 Subject: [PATCH 1/4] Improve Durable JSON serialization error logging --- azure/functions/durable_functions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure/functions/durable_functions.py b/azure/functions/durable_functions.py index a9cb88bf..a2c39141 100644 --- a/azure/functions/durable_functions.py +++ b/azure/functions/durable_functions.py @@ -99,10 +99,10 @@ def decode(cls, except json.JSONDecodeError: # String failover if the content is not json serializable result = data.value - except Exception: + except Exception as e: raise ValueError( 'activity trigger input must be a string or a ' - f'valid json serializable ({data.value})') + f'valid json serializable ({data.value})') from e else: raise NotImplementedError( f'unsupported activity trigger payload type: {data_type}') From 2fbafbf3aef388ed0597692610a2bcb0a1885af3 Mon Sep 17 00:00:00 2001 From: Andy Staples Date: Fri, 12 Sep 2025 11:30:25 -0700 Subject: [PATCH 2/4] Improve Durable JSON serialization error messages --- azure/functions/durable_functions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azure/functions/durable_functions.py b/azure/functions/durable_functions.py index a2c39141..4920830e 100644 --- a/azure/functions/durable_functions.py +++ b/azure/functions/durable_functions.py @@ -115,9 +115,9 @@ def encode(cls, obj: typing.Any, *, try: callback = _durable_functions._serialize_custom_object result = json.dumps(obj, default=callback) - except TypeError: + except TypeError as e: raise ValueError( - f'activity trigger output must be json serializable ({obj})') + f'activity trigger output must be json serializable ({obj})') from e return meta.Datum(type='json', value=result) From d3b6e3e8e4edaa932cf5ff84e58387cb3d0e2954 Mon Sep 17 00:00:00 2001 From: Andy Staples Date: Mon, 15 Sep 2025 12:47:24 -0600 Subject: [PATCH 3/4] Add tests --- tests/test_durable_functions.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/test_durable_functions.py b/tests/test_durable_functions.py index f8382862..2230aee0 100644 --- a/tests/test_durable_functions.py +++ b/tests/test_durable_functions.py @@ -165,6 +165,19 @@ def test_activity_trigger_encode(self): expected_type=type(datum['output'])) self.assertEqual(encoded, datum['expected_value']) + def test_activity_trigger_encode_failure_exception_has_cause(self): + class NonEncodable: + def __init__(self): + self.value = 'foo' + + data = NonEncodable() + + try: + ActivityTriggerConverter.encode(data, expected_type=None) + except ValueError as e: + self.assertIsNotNone(e.__cause__) + self.assertIsInstance(e.__cause__, TypeError) + def test_activity_trigger_decode(self): # Activity Trigger allow inputs to be any JSON serializables # The input values to the trigger should be passed into arguments @@ -209,6 +222,21 @@ def test_activity_trigger_decode(self): trigger_metadata=None) self.assertEqual(decoded, datum['expected_value']) + def test_activity_trigger_decode_failure_exception_has_cause(self): + class NonDecodable: + def __init__(self): + self.value = 'foo' + + data = Datum('{"value": "bar"}', 'json') + + try: + ActivityTriggerConverter.decode( + data=data, + trigger_metadata=None) + except ValueError as e: + self.assertIsNotNone(e.__cause__) + self.assertIsInstance(e.__cause__, TypeError) + def test_activity_trigger_has_implicit_return(self): self.assertTrue( ActivityTriggerConverter.has_implicit_output() From b1145d2a99f1c5185486decfd25b472154f590ca Mon Sep 17 00:00:00 2001 From: Andy Staples Date: Mon, 15 Sep 2025 12:49:26 -0600 Subject: [PATCH 4/4] Remove unused test code --- tests/test_durable_functions.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/test_durable_functions.py b/tests/test_durable_functions.py index 2230aee0..a81648be 100644 --- a/tests/test_durable_functions.py +++ b/tests/test_durable_functions.py @@ -223,10 +223,6 @@ def test_activity_trigger_decode(self): self.assertEqual(decoded, datum['expected_value']) def test_activity_trigger_decode_failure_exception_has_cause(self): - class NonDecodable: - def __init__(self): - self.value = 'foo' - data = Datum('{"value": "bar"}', 'json') try: