[Bug] Sandboxed datetime hints can fail decoding and drop signals
維護者通常 1 天內回覆
評估
這個 Issue 還沒有評估資料。
描述
What are you really trying to do?
Use a datetime workflow parameter, or a dataclass containing one, with the default data converter and default sandboxed workflow runner. The README lists datetime.datetime as supported.
Describe the bug
A bare datetime hint evaluated when the sandbox reimports the workflow module can be a restricted proxy. The default converter checks hint is datetime, so it rejects that hint with:
Unserializable type during conversion: <class 'datetime.datetime'>
At d488ff59, the identity check is in temporalio/converter/_payload_converter.py:849. The default restrictions cover datetime.datetime.now, today, and utcnow (temporalio/worker/workflow_sandbox/_restrictions.py:608–613), and attribute access creates the proxy (:1033–1051). The proxy compares equal to the native class but is a different object.
Observed on that commit:
| Boundary | Result |
|---|---|
Workflow run argument: bare datetime, list[datetime], or dataclass field |
Workflow tasks repeatedly fail with Failed decoding arguments |
| Signal argument | Signal is recorded in history, but the worker logs Failed deserializing signal input ... dropping the signal; the handler does not run |
| Update argument | WorkflowUpdateFailedError |
| Activity or child workflow result | Workflow tasks fail during decoding |
| Query argument | WorkflowQueryFailedError |
The run, signal, and update tests pass when their defining module is passed through or when using UnsandboxedWorkflowRunner. A separate passed-through dataclass model also works. For passthrough, the relevant module is the user module defining the workflow or model.
Run-argument failures were also reproduced on released SDK versions 1.35.0, 1.30.0, and 1.20.0.
Not every datetime-containing hint fails. In Python 3.12.3 probes, datetime | None held the native class and decoded successfully. Optional[datetime] and Union[datetime, str] depended on typing-alias creation order: with the sandbox creating the alias first, the former failed and the latter returned a string.
Minimal Reproduction
Save as datetime_hint_repro.py and run it as a Python script.
import asyncio
import uuid
from datetime import datetime, timezone
import temporalio.testing
import temporalio.worker
import temporalio.workflow
@temporalio.workflow.defn
class Greet:
@temporalio.workflow.run
async def run(self, when: datetime) -> str:
return when.isoformat()
async def main() -> None:
async with await temporalio.testing.WorkflowEnvironment.start_local() as env:
async with temporalio.worker.Worker(
env.client, task_queue="tq", workflows=[Greet]
):
h = await env.client.start_workflow(
Greet.run,
datetime(2021, 1, 2, tzinfo=timezone.utc),
id=f"w-{uuid.uuid4()}",
task_queue="tq",
)
try:
print("RESULT:", await asyncio.wait_for(h.result(), 8))
except asyncio.TimeoutError:
async for ev in h.fetch_history_events():
if ev.HasField("workflow_task_failed_event_attributes"):
f = ev.workflow_task_failed_event_attributes.failure
while f.HasField("cause"):
f = f.cause
print("WORKFLOW TASK FAILED:", f.message)
break
await h.terminate()
if __name__ == "__main__":
asyncio.run(main())
Expected:
RESULT: 2021-01-02T00:00:00+00:00
Saved output on d488ff59:
WORKFLOW TASK FAILED: Unserializable type during conversion: <class 'datetime.datetime'>
Changing the identity check to equality produces the expected result, but that prototype has the matching and replay problems described below.
Environment/Versions
- Linux x86-64.
- SDK source at
d488ff59; Python 3.12.3 for the minimal reproduction. Targeted regression tests also reproduced the failure on Python 3.10.22, 3.13.18, and 3.14.9. - Local dev server started by
WorkflowEnvironment.start_local(). The original reproduction logged Temporal CLI 1.9.1 / Server 1.32.0; the replay-history recording logged CLI 1.10.0 / Server 1.32.2.
Additional context
I would like to agree on the matching and replay approach before preparing a PR.
For matching:
hint == datetimemakes the reproductions decode, but invokes arbitrary type-hint equality. A supported dataclass with a custom metaclass could raise during comparison or compare equal and be misclassified. I would not submit that comparison unchanged.- A narrower approach is to recognize only the SDK's own proxy wrapping the native datetime class, using identity.
RestrictionContext.unwrap_if_proxied(_restrictions.py:815–819) provides that primitive. Custom converters should retain their precedence.
For replay, I recorded four datetime-signal workflows and one string-signal control on d488ff59. All five replay on the original code. With the equality prototype, two fail: a workflow that previously took a timeout branch encounters TimerMachine ... CancelTimerCommandSent ... TimerFired, and a signal handler that starts an activity produces [TMPRL1100] Nondeterminism error: Activity machine does not handle this event. The other three replay without errors, although that does not establish unchanged handler execution. In the reverse direction, three of five prototype-recorded histories fail on the original code.
The replay choices are:
- Apply corrected decoding unconditionally, explicitly accepting and documenting these compatibility breaks.
- Guard corrected decoding with a workflow logic flag, preserving old decoding during unflagged replay and recording use of the new behavior when enabled for live execution. A staged rollout, like flag 2's recognition-before-enablement period, would address compatibility with older workers. The policy also needs to cover typed memo and last-completion-result reads, which bypass
_convert_payloads(_workflow_instance.py:1400,:2063).
I favor the narrow identity check and compatibility flag. I have regression tests and saved replay histories for the original prototype; the guarded implementation still needs implementation and validation. I can prepare a PR for the agreed approach.
AI disclosure: this was prepared with AI assistance (Claude Opus 5.5 and Claude Sonnet 5.5).
- 主要語言
- Python
- 星號
- 1.2k
- 分支
- 285
- 平均合併
- 1 天 23 小時
- 30 天內合併 PR
- 80
環境準備
- 沒有 Dockerfile 或 Docker Compose 檔案
- 沒有 Pull Request 範本
- 閱讀貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
temporalio/sdk-python 的其他 Issue
-
[Bug] Deadlock interruption can leak a logging.Handler lock on Python ≤ 3.12 and freeze the worker未關閉
難度 4/5 3-5 天 新手友好度 47/100
temporalio/sdk-python#2095 · 1 則留言 ·
維護者通常 1 天內回覆
-
難度 4/5 3-5 天 新手友好度 58/100
temporalio/sdk-python#2092 ·
維護者通常 1 天內回覆
-
難度 4/5 3-5 天 新手友好度 18/100
temporalio/sdk-python#2080 ·
維護者通常 1 天內回覆
-
[Bug] TaskGroup cancellation of mixed Temporal operations can break replay可能已有人在做 @VegetarianOrc 於 3 天前認領。 未關閉sdk-python
temporalio/sdk-python#2069 · 已指派 1 人 ·
維護者通常 1 天內回覆
-
sdk-python
難度 4/5 3-5 天 新手友好度 22/100
temporalio/sdk-python#2067 · 2 則留言 ·
維護者通常 1 天內回覆
查看 temporalio/sdk-python 的全部 Issue
相似的 Issue
-
Remove the `pull_request_target` workflow before GitHub blocks it可能已有人在做 @jat255 今天認領。 未關閉
難度 2/5 1-3 小時 新手友好度 78/100
維護者通常 2 天內回覆
-
難度 2/5 1-3 小時 新手友好度 85/100
IBM/mcp-context-forge#7483 ·
維護者通常 2 天內回覆
-
bug good first issue
難度 2/5 1-3 小時 新手友好度 78/100
-
[Bug Report]: OpenAI 辅助 API 连通性测试因输出上限仅 1 token 误报“未知错误”可能已有人在做 @fjfanjian 今天認領。 未關閉
難度 2/5 1-3 小時 新手友好度 72/100
Project-N-E-K-O/N.E.K.O#3640 ·
維護者通常 1 天內回覆
-
author-page correction
難度 2/5 1-3 小時 新手友好度 64/100
acl-org/acl-anthology#10808 · 1 則留言 ·
維護者通常 1 天內回覆