Repository navigation
Description
Observed Behavior
FileCheckpointStorage.save() rejects a normal datetime with an IANA ZoneInfo timezone even when its timezone class is explicitly included in allowed_checkpoint_types. The suggested extra-type configuration changes the error from a blocked zoneinfo:ZoneInfo class to a blocked zoneinfo:ZoneInfo._unpickle attribute; it does not make this value checkpointable.
This also affects the public workflow path. An Executor requesting approval for a ZoneInfo-aware appointment reaches IDLE_WITH_PENDING_REQUESTS, but its file-backed pause checkpoints fail, leaving no saved checkpoint for another workflow instance to restore. The pending request and same-instance response remain usable. An in-memory checkpoint backend and file checkpoints using datetime.timezone.utc or a fixed UTC offset work in the corresponding controls.
Expected Behavior
An explicitly allowlisted standard-library timezone should have a supported way to round-trip inside an otherwise supported datetime. If this timezone representation is intentionally unsupported, the public extra-type guidance needs to explain that restriction and a usable persistence representation.
The restricted global and attribute traversal checks from #5923 should remain in place. The compatibility approach for this specific standard-library value needs maintainer agreement.
Steps to Reproduce
- Run the current Core source from commit
deecbcb062198f926516f89b65e5b1676e4514c2, with IANA timezone data available (tzdatais needed on this Windows environment). - Create
FileCheckpointStoragewithallowed_checkpoint_types=['zoneinfo:ZoneInfo']. - Save a
WorkflowCheckpointcontaining an ordinarydatetime(..., tzinfo=ZoneInfo('UTC')). - Observe the exception below. Replacing the timezone with
datetime.timezone.utclets the same save/load sequence succeed.
Minimal Reproduction
import asyncio
from datetime import datetime
from tempfile import TemporaryDirectory
from zoneinfo import ZoneInfo
from agent_framework import FileCheckpointStorage, WorkflowCheckpoint
async def main():
with TemporaryDirectory() as folder:
storage = FileCheckpointStorage(
folder,
allowed_checkpoint_types=['zoneinfo:ZoneInfo'],
)
checkpoint = WorkflowCheckpoint(
workflow_name='appointment',
graph_signature_hash='example',
state={'appointment': datetime(2026, 10, 15, 12, 30, tzinfo=ZoneInfo('UTC'))},
)
await storage.save(checkpoint)
asyncio.run(main())Error Messages and Stack Traces
agent_framework.exceptions.WorkflowCheckpointException: Failed to decode pickled checkpoint data: Checkpoint deserialization blocked for non-type attribute 'zoneinfo:ZoneInfo._unpickle'.
The exception occurs during save-time restore validation, before a checkpoint file is written. In the workflow case it is logged as a checkpoint warning; it does not abort ordinary execution. A completed two-node workflow can still save its later empty-state checkpoint after the date has left its pending messages. The reproduction above concerns saving a checkpoint while the date is still present.
Package Versions
Core executable source: 1.22.0 at deecbcb062198f926516f89b65e5b1676e4514c2; tzdata==2026.5. This was tested through direct source imports in an existing environment whose installed agent-framework-core distribution metadata is 1.19.0. The imported Core files were checked against the current commit's Git blobs; this is not a fresh dependency-lock installation.
Python Version
CPython 3.14.3.
Operating System
Windows.
Regression
Unknown. No earlier revision was executed for this report.
Additional Context
- Direct public storage checks cover naive dates, fixed UTC, a fixed offset,
ZoneInfo('UTC'), andZoneInfo('Europe/Paris')with memory, default file, and explicitly allowlisted file configurations. Eleven observations round-trip; the four ZoneInfo/file observations reject at save time. - A separate six-case approval workflow check preserves the request and a
Falseresponse in the same runtime. Both ZoneInfo/file pause cases have no persisted checkpoint; the other four controls restore correctly in a fresh workflow instance. These checks overlap with the storage checks and are not additional independent affected cases. - The normal security restrictions are documented in the current checkpoint implementation and the restricted attribute resolver. Python: Restrict checkpoint unpickler globals and attribute traversal #7432/Python: fix(core): restrict unpickler module-prefix allowlist to types only #5923 address the original unsafe traversal; Python: Add a global workflow checkpoint type registry #7636 adds the process-wide type registry. This report concerns the remaining ordinary
ZoneInfopersistence behavior. - No product patch has been prepared. Full Core/workspace tests, other operating systems, cloud checkpoint backends, live models, and performance were not tested.
- AI-assisted analysis, reproduction, and writing.
Acknowledgements
- I searched existing issues and did not find a duplicate.
- I personally verified this behavior and the reproduction details are authentic.
- I will wait for explicit maintainer agreement before starting implementation of a non-trivial change.
Metadata
Metadata
Labels
Type
Projects
- StatusShow more project fieldsNo status
Activity
github-actions commentedon Oct 16, 2026
🤖 Automated triage reproduction notes (agent-authored — trust but verify)
Agent analysis
The failure occurs in
python/packages/core/agent_framework/_workflows/_checkpoint_encoding.py::_RestrictedUnpickler._restricted_getattr, where save-time restore validation rejectszoneinfo:ZoneInfo._unpicklewhen adatetimecontains an explicitly allowlistedZoneInfo. Minimal repro: createFileCheckpointStorage(..., allowed_checkpoint_types=["zoneinfo:ZoneInfo"]), place adatetime(..., tzinfo=ZoneInfo("UTC"))inWorkflowCheckpoint.state, and callsave().python/packages/core/tests/workflow/test_checkpoint.py1.22.01.22.0he-yufeng commentedon Oct 17, 2026
Reproduced on current main (103229b) with a two-tier check, matching the report exactly.
Mechanism:
ZoneInfo.__reduce__()returns the bound class hookZoneInfo._unpickle, so the pickle stream reconstructs viagetattr(ZoneInfo, "_unpickle"). In_RestrictedUnpicklerthat lands in_restricted_getattr, where the resolved attribute is a bound method, not a type, so even withallowed_checkpoint_types=["zoneinfo:ZoneInfo"]the load dies on "blocked for non-type attribute 'zoneinfo:ZoneInfo._unpickle'". Verified live: bare allowlist fails at thezoneinfo:ZoneInfoglobal, and adding it moves the failure to the_unpickletraversal.On approach, my take is a narrow version of (a): in
_restricted_getattr, whenobjis an allowed type, also permit resolving its bound_unpicklereconstruction hook by that exact name. The safety argument is that for an already-allowlisted class this hook grants no capability beyond the constructor the operator explicitly opted into:ZoneInfo._unpickle("Europe/Paris", 1)returns the identical cached object asZoneInfo("Europe/Paris"). Keying the exception to the_unpicklename keeps arbitrary private attribute traversal blocked, and the #5923 global and traversal restrictions stay intact for everything else.The (b) alternative, special-casing IANA keys in the checkpoint encoder/decoder, avoids touching the unpickler at all but adds a parallel representation that has to be versioned and kept in sync with pickle's own layout for
datetime.Happy to send the (a) patch with tests (allowlisted ZoneInfo round-trip, plus negative pins that
_unpickleon a non-allowlisted class and other private attributes still raise) if that direction works for you.eavanvalkenburg commentedon Oct 27, 2026
Thanks for the detailed report. Safe roundtripping of ordinary datetime values with an explicitly allowed standard-library ZoneInfo timezone makes sense as a core checkpoint capability, and the team will consider it. We will plan the supported representation and trust/compatibility contract before implementation. This does not approve a generic exception for reconstruction methods on allowlisted classes, or promise implementation or timing.
eavanvalkenburg commentedon Oct 27, 2026
/plan Design narrowly scoped safe checkpoint roundtripping for datetime values containing an explicitly allowed standard-library zoneinfo.ZoneInfo. Compare a dedicated representation with an exact verified ZoneInfo-only reconstruction path; do not generally allow methods named _unpickle on arbitrary allowlisted classes or weaken restricted global/attribute traversal. Define trust, compatibility and missing-tzdata behavior, preserve datetime/timezone semantics including DST fold, and cover fresh-process approval-pause recovery plus existing nested-type/enum support and negative traversal controls. This is a supported-value/codec design decision, not a minor-ready patch; coordinate any contributor implementation only after that contract is agreed.