Microsoft Agent Framework: FileCheckpointStorage can't save a ZoneInfo datetime ("Checkpoint deserialization blocked")
A workflow checkpoint with a ZoneInfo-aware datetime fails to save to file, even with zoneinfo:ZoneInfo allowlisted. Repro on 1.25.0 and what to store instead.
Disclosure: I work on MemorySync (I'm Mohammed, the founder). I drafted this page with AI assistance and checked it against the GitHub issue listed under Sources on Nov 11, 2026. Code status: I ran the storage repro and the workaround offline on agent-framework-core 1.25.0 (Python 3.13, Linux, no model, no network). I didn't run a full workflow with a pending approval request, the in-memory or cloud checkpoint backends, or any MemorySync code.
Your Microsoft Agent Framework workflow keeps an appointment time in its state, as a datetime with a ZoneInfo timezone. With FileCheckpointStorage, saving the checkpoint fails:
WorkflowCheckpointException: Failed to decode pickled checkpoint data: Checkpoint deserialization blocked for type 'zoneinfo:ZoneInfo'. To allow this type, either include its 'module:qualname' key in the 'allowed_types' set passed to 'decode_checkpoint_value', or add it to 'allowed_checkpoint_types' on your checkpoint storage (for example, 'FileCheckpointStorage.allowed_checkpoint_types').
You follow the message and add zoneinfo:ZoneInfo to allowed_checkpoint_types. It still fails, with a different message:
WorkflowCheckpointException: Failed to decode pickled checkpoint data: Checkpoint deserialization blocked for non-type attribute 'zoneinfo:ZoneInfo._unpickle'.
That's microsoft/agent-framework issue #9440, "Python: [Bug]: Explicitly allowed ZoneInfo dates cannot be saved in file checkpoints".
MemorySync doesn't fix this bug. It's in how the framework's file checkpoints restrict what can be unpickled, and you need the workaround below with or without MemorySync.
Skip the memory plumbing around this fix. Workflow checkpoints hold run state. Once yours save cleanly, the MemorySync Microsoft Agent Framework guide shows a context provider for memory that lasts across sessions. It doesn't replace checkpoint storage. When you're ready, see MemorySync. Founding offer: use code FOUNDING30 for 30% off Starter for your first 3 months (Settings → Billing).
Status (Nov 11, 2026)
- #9440 was opened Oct 16, 2026 and is open. On Oct 27 a maintainer wrote that safe round-tripping of these values "makes sense as a core checkpoint capability, and the team will consider it", and that it "does not approve a generic exception for reconstruction methods on allowlisted classes".
- It reproduces on agent-framework-core 1.25.0, the version pip installed for me on Nov 11, 2026.
Step 1: Reproduce it
import asyncio
from datetime import datetime, timezone
from tempfile import TemporaryDirectory
from zoneinfo import ZoneInfo
from agent_framework import FileCheckpointStorage, WorkflowCheckpoint
async def try_save(label, value, allowed=None):
with TemporaryDirectory() as folder:
kw = {"allowed_checkpoint_types": allowed} if allowed else {}
storage = FileCheckpointStorage(folder, **kw)
cp = WorkflowCheckpoint(workflow_name="appointment", graph_signature_hash="example",
state={"appointment": value})
try:
await storage.save(cp)
loaded = await storage.load(cp.checkpoint_id)
print(label, "OK", repr(loaded.state["appointment"]))
except Exception as e:
print(label, type(e).__name__, str(e)[:120])
async def main():
paris = ZoneInfo("Europe/Paris")
await try_save("ZoneInfo, default", datetime(2026, 11, 10, 9, 0, tzinfo=paris))
await try_save("ZoneInfo, allowlisted", datetime(2026, 11, 10, 9, 0, tzinfo=paris), ["zoneinfo:ZoneInfo"])
await try_save("timezone.utc", datetime(2026, 11, 10, 8, 0, tzinfo=timezone.utc))
asyncio.run(main())
What I got on 1.25.0:
ZoneInfo, default WorkflowCheckpointException ... blocked for type 'zoneinfo:ZoneInfo' ...
ZoneInfo, allowlisted WorkflowCheckpointException ... blocked for non-type attribute 'zoneinfo:ZoneInfo._unpickle'.
timezone.utc OK datetime.datetime(2026, 11, 10, 8, 0, tzinfo=datetime.timezone.utc)
Why it happens
From the issue thread: file checkpoints are pickled, and save() checks that the data can be loaded again through a restricted unpickler, which only allows listed types. A ZoneInfo pickles as a call to ZoneInfo._unpickle, a method rather than a type, so the restricted loader blocks it even when the class itself is allowlisted. That check runs at save time, before the file is written, so no checkpoint is saved.
The issue also notes that in a workflow this is logged as a checkpoint warning rather than stopping the run, so the visible effect is a missing checkpoint: a workflow paused with a pending request can't be restored from file by another instance.
Step 2: Keep the zone name as plain data
Until the framework supports it, don't put ZoneInfo objects in checkpointed state. Store a string and the zone name, and rebuild the datetime where you use it:
from datetime import datetime
from zoneinfo import ZoneInfo
def pack(d: datetime) -> dict:
return {"at": d.isoformat(), "tz": d.tzinfo.key}
def unpack(p: dict) -> datetime:
return datetime.fromisoformat(p["at"]).astimezone(ZoneInfo(p["tz"]))
A dict like {"at": "2026-11-10T09:00:00+01:00", "tz": "Europe/Paris"} saved and loaded from FileCheckpointStorage without error in my run. unpack turns it back into a ZoneInfo-aware value, so date arithmetic after the restore still follows the zone's DST rules.
Two other options:
- UTC in state:
datetime.timezone.utcsaved and loaded fine in my run. Convert to the user's zone only for display or local-time arithmetic, and keep the zone name somewhere. - Keep
ZoneInfoout of the message, too: per the issue, the pending approval request carrying the date is what gets checkpointed while the workflow waits, so apply the same packing to request payloads, not only to state.
Don't work around it by allowlisting more internals of zoneinfo. The restriction exists for security reasons, and the maintainer comment above says they don't plan a generic exception for methods like _unpickle.
Skip the plumbing with MemorySync
Disclosure: I build MemorySync.
This doesn't change the fix above: workflow checkpoints still go through FileCheckpointStorage, so keep zones as plain data as in Step 2. The MemorySync Microsoft Agent Framework guide is about agent memory, not checkpoints. It documents MemorySyncContextProvider, which you pass to an Agent as context_providers=[provider]. Before each run it recalls what's stored for that user into the instructions, and after the run it sends the user's messages for fact extraction (the agent's replies aren't sent). It requires agent-framework-core 1.8+ and Python 3.10+ and lists agent-framework-memorysync 1.1.0.
From the guide:
Unverified (not run):
pip install agent-framework-memorysync
from agent_framework import Agent
from agent_framework_memorysync import MemorySyncContextProvider
provider = MemorySyncContextProvider(
user_id="customer-42", # the end user these memories belong to
session_id="support", # optional, tags this conversation's facts
)
agent = Agent(client=..., context_providers=[provider])
result = await agent.run("Which seat should I book?")
Snippet adapted from the MemorySync Microsoft Agent Framework guide; I haven't run it for this page. Set MEMORYSYNC_API_KEY in the environment, as the guide says.
Try it: MemorySync is optional here: the steps above work without it. If you want the memory layer from the snippet, see MemorySync, create an API key in the dashboard and drop the snippet into your agent.
FAQ
Does the in-memory checkpoint storage have this problem? The issue reports that the in-memory backend round-trips these values. I didn't test it. The file backend is the one that applies the restricted load at save time.
Does ZoneInfo("UTC") work?
No, per the issue it fails the same way, because it's still a ZoneInfo. Use datetime.timezone.utc for UTC.
Will an upgrade fix it? Not as of Nov 11, 2026 (1.25.0 still blocks it). Watch #9440 for the supported representation the maintainers plan.
Sources
- github.com/microsoft/agent-framework/issues/9440 (opened 2026-10-16, open, 4 comments; repro,
ZoneInfo._unpicklemechanism, in-memory and fixed-offset controls, maintainer comments of 2026-10-27; read 2026-10-30) - PyPI release info for agent-framework-core 1.25.0 (read 2026-11-11)
- Offline runs on agent-framework-core 1.25.0, Python 3.13 (2026-11-11)
- MemorySync Microsoft Agent Framework guide: docs.memorysync.io/guides/agent-framework (fetched 2026-10-30)
Try it
For an overview of MemorySync, see the MemorySync homepage.
Next step: read the MemorySync Microsoft Agent Framework guide, then see MemorySync. Founding offer: use code FOUNDING30 for 30% off Starter for your first 3 months (Settings → Billing).
About the author
- Building MemorySync (memorysync.io) — persistent memory infrastructure for AI agents, Cursor, and Claude Code.
Comments
Join the discussionNo comments yet. Be the first to comment.
