Skip to content

Commit 154aab9

Browse files
authored
Fix issue when failing to convert failure (#727)
Fixes #685
1 parent fe46e1a commit 154aab9

File tree

3 files changed

+84
-0
lines changed

3 files changed

+84
-0
lines changed

temporalio/worker/_activity.py

+1
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,7 @@ async def _run_activity(
508508
completion.result.failed.failure.message = (
509509
f"Failed building exception result: {inner_err}"
510510
)
511+
completion.result.failed.failure.application_failure_info.SetInParent()
511512

512513
# Do final completion
513514
try:

temporalio/worker/_workflow_instance.py

+1
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,7 @@ def activate(
442442
self._current_completion.failed.failure.message = (
443443
f"Failed converting activation exception: {inner_err}"
444444
)
445+
self._current_completion.failed.failure.application_failure_info.SetInParent()
445446

446447
def is_completion(command):
447448
return (

tests/worker/test_workflow.py

+82
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
)
7575
from temporalio.converter import (
7676
DataConverter,
77+
DefaultFailureConverter,
7778
DefaultFailureConverterWithEncodedAttributes,
7879
DefaultPayloadConverter,
7980
PayloadCodec,
@@ -84,6 +85,7 @@
8485
ApplicationError,
8586
CancelledError,
8687
ChildWorkflowError,
88+
FailureError,
8789
TemporalError,
8890
TimeoutError,
8991
WorkflowAlreadyStartedError,
@@ -6451,6 +6453,86 @@ async def test_concurrent_sleeps_use_proper_options(
64516453
await handle.query("__temporal_workflow_metadata")
64526454

64536455

6456+
class BadFailureConverterError(Exception):
6457+
pass
6458+
6459+
6460+
class BadFailureConverter(DefaultFailureConverter):
6461+
def to_failure(
6462+
self,
6463+
exception: BaseException,
6464+
payload_converter: PayloadConverter,
6465+
failure: Failure,
6466+
) -> None:
6467+
if isinstance(exception, BadFailureConverterError):
6468+
raise RuntimeError("Intentional failure conversion error")
6469+
super().to_failure(exception, payload_converter, failure)
6470+
6471+
6472+
@activity.defn
6473+
async def bad_failure_converter_activity() -> None:
6474+
raise BadFailureConverterError
6475+
6476+
6477+
@workflow.defn(sandboxed=False)
6478+
class BadFailureConverterWorkflow:
6479+
@workflow.run
6480+
async def run(self, fail_workflow_task) -> None:
6481+
if fail_workflow_task:
6482+
raise BadFailureConverterError
6483+
else:
6484+
await workflow.execute_activity(
6485+
bad_failure_converter_activity,
6486+
schedule_to_close_timeout=timedelta(seconds=30),
6487+
retry_policy=RetryPolicy(maximum_attempts=1),
6488+
)
6489+
6490+
6491+
async def test_bad_failure_converter(client: Client):
6492+
config = client.config()
6493+
config["data_converter"] = dataclasses.replace(
6494+
config["data_converter"],
6495+
failure_converter_class=BadFailureConverter,
6496+
)
6497+
client = Client(**config)
6498+
async with new_worker(
6499+
client, BadFailureConverterWorkflow, activities=[bad_failure_converter_activity]
6500+
) as worker:
6501+
# Check activity
6502+
with pytest.raises(WorkflowFailureError) as err:
6503+
await client.execute_workflow(
6504+
BadFailureConverterWorkflow.run,
6505+
False,
6506+
id=f"workflow-{uuid.uuid4()}",
6507+
task_queue=worker.task_queue,
6508+
)
6509+
assert isinstance(err.value.cause, ActivityError)
6510+
assert isinstance(err.value.cause.cause, ApplicationError)
6511+
assert (
6512+
err.value.cause.cause.message
6513+
== "Failed building exception result: Intentional failure conversion error"
6514+
)
6515+
6516+
# Check workflow
6517+
handle = await client.start_workflow(
6518+
BadFailureConverterWorkflow.run,
6519+
True,
6520+
id=f"workflow-{uuid.uuid4()}",
6521+
task_queue=worker.task_queue,
6522+
)
6523+
6524+
async def task_failed_message() -> Optional[str]:
6525+
async for e in handle.fetch_history_events():
6526+
if e.HasField("workflow_task_failed_event_attributes"):
6527+
return e.workflow_task_failed_event_attributes.failure.message
6528+
return None
6529+
6530+
await assert_eq_eventually(
6531+
"Failed converting activation exception: Intentional failure conversion error",
6532+
task_failed_message, # type: ignore
6533+
)
6534+
6535+
64546536
@workflow.defn
64556537
class SignalsActivitiesTimersUpdatesTracingWorkflow:
64566538
"""

0 commit comments

Comments
 (0)