Skip to content

fix: pre-bind handle variables to avoid Python 3.14 NameError in closure cells#1528

Open
rmotgi1227 wants to merge 1 commit into
temporalio:mainfrom
rmotgi1227:fix/py314-activity-handle-unbound-cell-1517
Open

fix: pre-bind handle variables to avoid Python 3.14 NameError in closure cells#1528
rmotgi1227 wants to merge 1 commit into
temporalio:mainfrom
rmotgi1227:fix/py314-activity-handle-unbound-cell-1517

Conversation

@rmotgi1227
Copy link
Copy Markdown

Summary

Closes #1517.

Python 3.14 changed asyncio.Task to eagerly inspect a coroutine's closure cells when task.set_name() is called (CPython gh-126004). If a cell is unbound at that moment the interpreter raises:

NameError: cannot access free variable 'handle' where it is not
associated with a value in enclosing scope

Three methods in _workflow_instance.py had this pattern:

handle: _ActivityHandle          # annotation only — cell is EMPTY

async def run_activity() -> Any:
    handle._started = True       # closure over the empty cell

handle = _ActivityHandle(self, input, run_activity())
# ↑ constructor calls _register_task → task.set_name()
#   Python 3.14 inspects run_activity's cells → NameError

The same pattern exists in _outbound_start_child_workflow (_ChildWorkflowHandle) and _outbound_start_nexus_operation (_NexusOperationHandle).

Fix

Replace the bare annotation with a cast-annotated assignment:

handle = cast(_ActivityHandle, None)   # cell is now BOUND (to None)

cast is already imported; it is a no-op at runtime (returns its second argument) but preserves the static type. By the time the coroutine actually runs, the outer handle = _ActivityHandle(...) line has completed and the variable holds the real object, so no code ever observes the temporary None.

Changes

temporalio/worker/_workflow_instance.py

Location Handle type Change
_outbound_schedule_activity _ActivityHandle handle: _ActivityHandlehandle = cast(_ActivityHandle, None)
_outbound_start_child_workflow _ChildWorkflowHandle same pattern
_outbound_start_nexus_operation _NexusOperationHandle[OutputT] same pattern

Test plan

…alio#1517)

Python 3.14 changed asyncio.Task to eagerly inspect a coroutine's
closure cells when task.set_name() is called (CPython issue gh-126004).
If a closure cell is unbound at that moment the interpreter raises:

  NameError: cannot access free variable 'handle' where it is not
  associated with a value in enclosing scope

Three places in _outbound_schedule_activity, _outbound_start_child_workflow,
and _outbound_start_nexus_operation all declared 'handle' as a bare type
annotation (no binding), created a coroutine that closes over it, and then
passed that coroutine to the handle's constructor -- which triggered task
naming before the outer assignment completed.

Fix: initialise each handle variable to cast(<Type>, None) immediately
before defining the coroutine.  The cell is now bound (to None) so the
eager inspection succeeds.  The coroutine is only ever awaited after the
outer handle = <Constructor>(...) line completes, so by the time any
code inside run_activity / run_child / operation_handle_fn executes the
variable holds the correct object.
@rmotgi1227 rmotgi1227 requested a review from a team as a code owner May 14, 2026 20:13
@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


Rishab Motgi seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

1 similar comment
@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


Rishab Motgi seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Title: Python 3.14 Compatibility: NameError in _outbound_schedule_activity due to eager closure-cell inspection

2 participants