Skip to content

Commit 218ef5e

Browse files
committed
fix: guard CancelledError re-raise with _cancel_requested check (fixes #1445)
1 parent f604f18 commit 218ef5e

2 files changed

Lines changed: 57 additions & 6 deletions

File tree

temporalio/worker/_workflow_instance.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2025,7 +2025,8 @@ async def run_child() -> Any:
20252025
return handle
20262026
except asyncio.CancelledError:
20272027
apply_child_cancel_error()
2028-
raise
2028+
if self._cancel_requested:
2029+
raise
20292030

20302031
async def _outbound_start_nexus_operation(
20312032
self, input: StartNexusOperationInput[Any, OutputT]
@@ -2052,7 +2053,6 @@ async def operation_handle_fn() -> OutputT:
20522053
except asyncio.CancelledError:
20532054
cancel_command = self._add_command()
20542055
handle._apply_cancel_command(cancel_command)
2055-
raise
20562056

20572057
handle = _NexusOperationHandle(
20582058
self, self._next_seq("nexus_operation"), input, operation_handle_fn()
@@ -2067,7 +2067,9 @@ async def operation_handle_fn() -> OutputT:
20672067
except asyncio.CancelledError:
20682068
cancel_command = self._add_command()
20692069
handle._apply_cancel_command(cancel_command)
2070-
raise
2070+
if self._cancel_requested:
2071+
raise
2072+
20712073

20722074
#### Miscellaneous helpers ####
20732075
# These are in alphabetical order.

tests/worker/test_workflow.py

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1209,9 +1209,58 @@ async def test_workflow_cancel_child_started(client: Client, use_execute: bool):
12091209
assert isinstance(err.value.cause.cause, CancelledError)
12101210

12111211

1212-
@pytest.mark.skip(reason="unable to easily prevent child start currently")
1213-
async def test_workflow_cancel_child_unstarted(_client: Client):
1214-
raise NotImplementedError
1212+
@workflow.defn
1213+
class CancelDuringChildStartWorkflow:
1214+
def __init__(self) -> None:
1215+
self._proceed = False
1216+
1217+
@workflow.signal
1218+
def proceed(self) -> None:
1219+
self._proceed = True
1220+
1221+
@workflow.run
1222+
async def run(self) -> None:
1223+
await workflow.wait_condition(lambda: self._proceed)
1224+
# Start a child on a task queue with no worker. The child's first WFT
1225+
# never starts, so _start_fut remains unresolved and the start loop
1226+
# blocks forever.
1227+
await workflow.start_child_workflow(
1228+
LongSleepWorkflow.run,
1229+
id=f"{workflow.info().workflow_id}_child",
1230+
task_queue="nonexistent-task-queue-no-worker-abc123",
1231+
)
1232+
await workflow.sleep(1000)
1233+
1234+
1235+
async def test_workflow_cancel_child_unstarted(client: Client):
1236+
# Regression test for https://github.com/temporalio/sdk-python/issues/1445
1237+
#
1238+
# When cancellation arrived while the parent was waiting for a child
1239+
# workflow to start, the CancelledError was caught in the start loop
1240+
# to send a cancel command to the child — but was not re-raised.
1241+
# Because _start_fut never resolves (child on a queue with no worker),
1242+
# the loop would keep waiting forever, hanging the parent workflow.
1243+
#
1244+
# The fix: re-raise only when self._cancel_requested is True, which
1245+
# distinguishes Temporal workflow cancellation from other CancelledError
1246+
# sources such as asyncio.wait_for timeouts.
1247+
async with new_worker(
1248+
client,
1249+
CancelDuringChildStartWorkflow,
1250+
# Deliberately not registering LongSleepWorkflow and not starting
1251+
# a worker on the child's task queue.
1252+
) as worker:
1253+
handle = await client.start_workflow(
1254+
CancelDuringChildStartWorkflow.run,
1255+
id=f"workflow-{uuid.uuid4()}",
1256+
task_queue=worker.task_queue,
1257+
execution_timeout=timedelta(seconds=30),
1258+
)
1259+
await handle.signal(CancelDuringChildStartWorkflow.proceed)
1260+
await handle.cancel()
1261+
with pytest.raises(WorkflowFailureError) as err:
1262+
await handle.result()
1263+
assert isinstance(err.value.cause, CancelledError)
12151264

12161265

12171266
@workflow.defn

0 commit comments

Comments
 (0)