Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

[Bug] async def update validator is accepted but never runs, so every update is accepted

已关闭 适合新手
#2,004 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

已经有一个关联 PR 被合并了。

  • #2005 来自 @avanish-garg —— 已合并

评估

难度
2/5
预计耗时
1-3 小时
新手友好度
78/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
python
领域
backend

调研方向

从 temporalio/worker/_workflow_instance.py 中的 WorkflowInboundImpl.handle_update_validator 开始,然后沿着 _UpdateDefinition.set_validator 以及 decorator、set_update_handler 和 set_dynamic_update_handler 这些路径追踪 validator 的注册。先阅读相关的 update 和 query 验证测试。完成标准是:这些路径在注册时都会拒绝异步 validator,同时同步 validator 仍可正常工作。

由索引模型根据 Issue 内容生成。

描述

bug
What are you really trying to do?

Reject an update with invalid arguments before it is written to history, using an update validator on a workflow whose update handler is async def.

Describe the bug

@my_update.validator accepts an async def function without any error, but the validator never executes, so the update is always accepted.

The inbound interceptor calls the validator without awaiting it (WorkflowInboundImpl.handle_update_validator in temporalio/worker/_workflow_instance.py, handler(*input.args)), unlike handle_signal, handle_query and handle_update_handler, which check inspect.iscoroutinefunction and await. For an async def validator the call only creates a coroutine object, which is dropped. The only symptom is a RuntimeWarning: coroutine ... was never awaited on the worker, so a validator that is meant to reject bad input silently lets everything through.

The README says "Validators cannot be async", and the other definition-time mistakes are all reported when the workflow is defined (@workflow.run on a non-async function raises ValueError, duplicate handlers raise ValueError, a validator/handler signature mismatch raises ValueError since #723, async queries emit a DeprecationWarning). The async validator is the one case with no check at all. pyright and mypy do flag it statically, but nothing does at runtime.

The same happens through the dynamic registration paths, workflow.set_update_handler(name, handler, validator=async_fn) and workflow.set_dynamic_update_handler(handler, validator=async_fn).

Expected: a ValueError when the validator is registered (decorator time, as for the signature mismatch). Actual: no error, and the update with n=-5 below is accepted.

Minimal Reproduction
import asyncio, uuid

from temporalio import workflow
from temporalio.client import WorkflowUpdateFailedError
from temporalio.testing import WorkflowEnvironment
from temporalio.worker import UnsandboxedWorkflowRunner, Worker


@workflow.defn
class W:
    def __init__(self) -> None:
        self.done = False
        self.total = 0

    @workflow.run
    async def run(self) -> int:
        await workflow.wait_condition(lambda: self.done)
        return self.total

    @workflow.update
    async def add_sync(self, n: int) -> int:
        self.total += n
        return self.total

    @add_sync.validator
    def _v_sync(self, n: int) -> None:
        if n < 0:
            raise ValueError("negative")

    @workflow.update
    async def add_async(self, n: int) -> int:
        self.total += n
        return self.total

    @add_async.validator
    async def _v_async(self, n: int) -> None:  # accepted by the decorator
        if n < 0:
            raise ValueError("negative")

    @workflow.signal
    def finish(self) -> None:
        self.done = True


async def main():
    async with await WorkflowEnvironment.start_local() as env:
        async with Worker(
            env.client,
            task_queue="tq",
            workflows=[W],
            workflow_runner=UnsandboxedWorkflowRunner(),
        ):
            h = await env.client.start_workflow(W.run, id=str(uuid.uuid4()), task_queue="tq")
            try:
                await h.execute_update(W.add_sync, -5)
                print("sync validator : ACCEPTED -5 (unexpected)")
            except WorkflowUpdateFailedError as e:
                print("sync validator : rejected -5 ->", repr(e.cause)[:60])
            print("async validator: returned", await h.execute_update(W.add_async, -5))
            await h.signal(W.finish)
            print("final total:", await h.result())


asyncio.run(main())

Output on current main (646d70f5):

.../temporalio/worker/_workflow_instance.py:3264: RuntimeWarning: coroutine '_bind_method.<locals>.with_object' was never awaited
  handler(*input.args)
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
sync validator : rejected -5 -> ApplicationError('ValueError: negative')
async validator: returned -5 (update with n=-5 ACCEPTED; total is now negative)
final total: -5

(UnsandboxedWorkflowRunner is only used to keep the script short.)

Environment/Versions
  • OS and processor: M-series Mac (macOS 26.3.1, arm64)
  • Temporal Version: SDK main at 646d70f5 (pure Python from the repo, temporalio 1.34.0 wheel bridge), Python 3.12.14, dev server from WorkflowEnvironment.start_local() (Temporal CLI 1.9.1, server 1.32.0)
  • Are you using Docker or Kubernetes or building Temporal from source? No
Additional context

Proposed fix, in the PR that will follow: raise ValueError("Update validator must not be an async function") in _UpdateDefinition.set_validator, which the decorator and both set_update_handler / set_dynamic_update_handler go through. It is a (very small) behavior change, since a workflow that has an async def validator today would now fail at class definition instead of silently accepting every update. A DeprecationWarning like the one for async queries would keep that workflow loading, but it would keep the validation switched off, so I went with raising. Happy to switch to a warning if you prefer.

主要语言
Python
星标
1.2k
派生
276
平均合并
1 天 22 小时
30 天内合并 PR
85

环境准备

  • 没有 Dockerfile 或 Docker Compose 文件
  • 没有 Pull Request 模板
  • 阅读贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

temporalio/sdk-python 的其他 Issue

查看 temporalio/sdk-python 的全部 Issue

相似的 Issue

更多 Python Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。