[Bug] Sandboxed datetime hints can fail decoding and drop signals
Maintainer thường phản hồi trong vòng 1 ngày
Đánh giá
Issue này chưa được đánh giá.
Mô tả
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).
- Ngôn ngữ chính
- Python
- Star
- 1.2k
- Fork
- 285
- Merge trung bình
- 1 ngày 21 giờ
- Pull request đã merge (30 ngày)
- 80
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Không có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của temporalio/sdk-python
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 58/100
temporalio/sdk-python#2092 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 18/100
temporalio/sdk-python#2080 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
[Bug] TaskGroup cancellation of mixed Temporal operations can break replayCó thể đã có người làm @VegetarianOrc đã nhận 3 ngày trước. Đang mởsdk-python
temporalio/sdk-python#2069 · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
sdk-python
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 22/100
temporalio/sdk-python#2067 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
[Bug] lambda_worker otel.apply_defaults leaks a span exporter thread and gRPC channel on every warm invocationCó thể đã có người làm @avanish-garg đã nhận 8 ngày trước. Đang mởbug sdk-python
Độ khó 4/5 1-2 ngày Mức phù hợp với người mới 30/100
temporalio/sdk-python#2040 ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của temporalio/sdk-python
Issue tương tự
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
Textualize/textual#6753 · 1 bình luận ·
-
priority:low size:small
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
RonaldHensbergen/composable-data-stack#1177 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Use registered media types for HDF5 and NetCDF, and stop claiming media_type must be IANA-registeredĐang mởdocumentation metadata
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 87/100
Maintainer thường phản hồi trong vòng 1 ngày
-
check-typing: ty 0.0.98+ fails on the renamed rule `unresolved-import` (now `import-not-found`)Có thể đã có người làm @oberstet đã nhận hôm nay. Đang mở
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 91/100
crossbario/cfxdb#145 ·