Skip to content

Commit 1111666

Browse files
committed
Fix issue when failing to convert failure
Fixes temporalio#685
1 parent 7af48a7 commit 1111666

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,
@@ -6448,3 +6450,83 @@ async def test_concurrent_sleeps_use_proper_options(
64486450

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

0 commit comments

Comments
 (0)