# CrewAI memory + checkpointing: fixing 'Unable to serialize unknown type: ModelMetaclass' (no checkpoint written)

*Disclosure: I work on MemorySync (I'm Mohammed, the founder). I drafted this article with AI assistance and checked it against the GitHub issue, the open fix PR, the CrewAI docs, the CrewAI source on PyPI and the MemorySync docs listed under Sources on Oct 31, 2026. Code status: I ran the issue's own offline reproduction (scripted LLM and embedding responses, outbound connections blocked, no API keys) on crewai 1.15.28 and 1.15.33 with pydantic 2.12.5 on Python 3.12.15, then the same script with the workaround patch below, the `CheckpointFailedEvent` listener below, and a dump-and-restore check that mirrors the PR's test. I didn't run anything against a real model or embedder, the PR branch itself, a SQLite checkpoint provider, a hierarchical crew, or the MemorySync snippet.*

You turned on CrewAI's built-in `Memory` and checkpointing on the same crew. The crew runs fine, but no checkpoint file shows up, and your logs have this after every finished task:

```text
WARNING:crewai.state.checkpoint_listener:Auto-checkpoint failed for event task_completed
...
pydantic_core._pydantic_core.PydanticSerializationError: Unable to serialize unknown type: <class 'pydantic._internal._model_construction.ModelMetaclass'>
```

That's CrewAI issue [#8318](https://github.com/crewAIInc/crewAI/issues/8318), "Memory-enabled checkpoints fail because RecallFlow redeclares initial_state without the base Flow serializer", reported on crewai 1.15.28.

Short version: there's no released fix yet. The fix is in an open PR, [#8327](https://github.com/crewAIInc/crewAI/pull/8327). Until it ships, you can apply the same one-field change at startup (Step 3), or run checkpointed crews without `Memory`. Step 2 shows how to make the failure visible so it doesn't go unnoticed.

MemorySync doesn't fix this bug. The cause is in CrewAI's own memory flows, and plugging a different storage backend into `Memory` doesn't change that (more below). MemorySync comes up once, near the end.

> **Skip the memory plumbing.** The [MemorySync CrewAI guide](https://docs.memorysync.io/guides/crewai?utm_source=hashnode&utm_medium=organic_page&utm_campaign=v5_oct25&utm_content=pseo_crewai-memory-checkpoint-unable-to-serialize-unknown-type-modelmetaclass) shows a storage backend for CrewAI 1.x `Memory`, plus agent tools and a context helper. It doesn't fix this checkpoint bug. When you're ready, [see MemorySync](https://memorysync.io/?utm_source=hashnode&utm_medium=organic_page&utm_campaign=v5_oct25&utm_content=pseo_crewai-memory-checkpoint-unable-to-serialize-unknown-type-modelmetaclass). Founding offer: use code FOUNDING30 for 30% off Starter for your first 3 months (Settings → Billing).

## Status (Nov 13, 2026)

- [#8318](https://github.com/crewAIInc/crewAI/issues/8318) was opened Oct 20, 2026 (19:15 IST), is labeled `bug`, and is still open with no comments as of Nov 13, 2026.
- [#8327](https://github.com/crewAIInc/crewAI/pull/8327), "fix(memory): keep initial_state json serializer on memory flows", was opened the same day and says "Fixes #8318". As of Nov 13, 2026 it's still open and not merged.
- The latest release on PyPI is [crewai 1.15.41](https://pypi.org/project/crewai/) (published Nov 13, 2026, 00:12 IST). I checked its source and `RecallFlow` / `EncodingFlow` in `crewai/memory/` still redeclare `initial_state` without the serializer (a source read, not a run). I ran the reproduction on 1.15.28 and 1.15.33 only.

## Step 1: Confirm it's this bug

You're looking at this bug if all of these are true:

1. The crew has CrewAI's built-in `Memory` (for example `memory=Memory(...)` on the `Crew`).
2. Checkpointing is on (`checkpoint=True` or a `CheckpointConfig`), with the default `task_completed` trigger or another automatic event.
3. Your checkpoint folder stays empty (or has no new files), and the log shows `Auto-checkpoint failed` with `Unable to serialize unknown type` and `ModelMetaclass`.

Why it happens, per the issue and the PR: CrewAI memory runs two internal flows, `RecallFlow` (for recall) and `EncodingFlow` (for saving). Both redeclare the `initial_state` field as a plain class reference. That replaces the base `Flow` field, which carries a JSON serializer added in PR [#5600](https://github.com/crewAIInc/crewAI/pull/5600) to turn that class into something JSON can hold. Without the serializer, the checkpoint's JSON dump reaches a raw Pydantic model class and stops.

From the installed 1.15.33 source, `Memory.remember()` always creates an `EncodingFlow`, and `Memory.recall()` creates a `RecallFlow` unless you pass `depth="shallow"`. Agents call `recall()` with the default depth, so a crew with memory creates one of these flows almost immediately.

Here's what the issue's reproduction did in my runs. It completes the first task, deliberately fails the second, then tries to restore from the checkpoint with `Crew.fork()`:

| crewai | Memory | Checkpoint files written | Restore with `Crew.fork()` |
|---|---|---|---|
| 1.15.28 | none (control) | 1 | restored; only the second task ran, with the first task's saved output in its prompt |
| 1.15.28 | `Memory(...)` | 0 | nothing to restore |
| 1.15.33 | none (control) | 1 | same as the 1.15.28 control |
| 1.15.33 | `Memory(...)` | 0 | nothing to restore |

The crew itself doesn't stop. CrewAI's [checkpointing docs](https://docs.crewai.com/en/concepts/checkpointing) say automatic checkpoint writes behave this way on purpose: "a failed write is logged and the run continues." So the first time you notice may be when you try to resume and there's nothing to resume from.

## Step 2: Make the failure visible

A failed automatic write emits a `CheckpointFailedEvent` that carries the error text. Register a listener once at startup:

```python
from crewai.events.event_bus import crewai_event_bus
from crewai.events.types.checkpoint_events import CheckpointFailedEvent

@crewai_event_bus.on(CheckpointFailedEvent)
def on_checkpoint_failed(source, event):
    print("checkpoint failed:", event.trigger, event.error)
```

On 1.15.33 without the patch, this printed:

```text
checkpoint failed: task_completed Unable to serialize unknown type: <class 'pydantic._internal._model_construction.ModelMetaclass'>
```

Swap the `print` for your own alerting. With the patch from Step 3 loaded, it printed nothing in my run.

## Step 3: Workaround until the fix ships

The PR changes one field on each of the two memory flows so it keeps the base serializer. You can make the same change at runtime. Save this as `memory_checkpoint_patch.py` and import it once at startup, before you build any crew, in every process that writes or restores checkpoints:

```python
# Workaround for crewAI issue #8318 until PR #8327 ships.
from typing import Any

from pydantic import PlainSerializer

from crewai.flow.runtime import _serialize_initial_state  # private helper
from crewai.memory.encoding_flow import EncodingFlow
from crewai.memory.recall_flow import RecallFlow

for flow_cls in (RecallFlow, EncodingFlow):
    field = flow_cls.model_fields["initial_state"]
    if not any(isinstance(m, PlainSerializer) for m in field.metadata):
        field.metadata.append(
            PlainSerializer(_serialize_initial_state, return_type=Any, when_used="json")
        )
        flow_cls.model_rebuild(force=True)
```

What it did in my runs, on both 1.15.28 and 1.15.33:

| Check | Without the patch | With the patch |
|---|---|---|
| Issue's reproduction, memory case: checkpoint files written | 0 | 1 |
| Restore with `Crew.fork()` | nothing to restore | restored; only the second task ran, with the first task's saved output in its prompt |
| `Auto-checkpoint failed` lines in the log | 1 | 0 |
| Dump a runtime state holding a `RecallFlow`, then an `EncodingFlow`, to JSON and restore it (the PR's test) | both dumps fail with the error | both dump and restore |

Things to know before you use it:

- **It relies on a private helper**, `_serialize_initial_state`, and on CrewAI's internal flow classes. A CrewAI update can rename or move them, so pin your crewai version while the patch is in place and re-run your checkpoint check after each upgrade.
- **It's the PR's change, applied from outside.** I didn't install the PR branch; the patch adds the same serializer the PR adds.
- **Remove it once a release contains the fix.** The `if not any(...)` guard skips fields that already have a serializer, but the cleaner path is to delete the import after upgrading.
- **My end-to-end run used read-only memory**, as the issue's script does, so only `RecallFlow` ran during the crew. `EncodingFlow` (memory saves) was covered by the dump-and-restore check only.

**Or run checkpointed crews without `Memory`.** In the same reproduction, the crew without memory wrote its checkpoint and restored correctly on both versions. That's a real trade-off: you lose CrewAI's built-in recall for that crew.

## Step 4: Check that checkpoints are written again

After adding the patch (or upgrading to a release that includes PR #8327), run one short crew with memory and checkpointing on, then:

1. Confirm a new file appears in your checkpoint location (`./.checkpoints/` by default).
2. Confirm the Step 2 listener printed nothing and there's no `Auto-checkpoint failed` line in the log.
3. Restore from that file the way you do in production (`Crew.from_checkpoint(...)`, `crew.kickoff(from_checkpoint=...)` or `Crew.fork(...)`) and check that completed tasks are skipped. The [checkpointing docs](https://docs.crewai.com/en/concepts/checkpointing) describe the expected behavior: "Completed tasks are skipped, memory and knowledge are rehydrated".

## Skip the plumbing with MemorySync

Disclosure: I build MemorySync.

This doesn't fix the checkpoint bug. The [MemorySync CrewAI guide](https://docs.memorysync.io/guides/crewai?utm_source=hashnode&utm_medium=organic_page&utm_campaign=v5_oct25&utm_content=pseo_crewai-memory-checkpoint-unable-to-serialize-unknown-type-modelmetaclass) documents `MemorySyncStorage`, a CrewAI `StorageBackend` you pass to `Memory(storage=...)` so "the crew's automatic save/recall loop runs against MemorySync instead of local LanceDB files." Because it plugs into CrewAI's own `Memory`, recall and saves still go through `RecallFlow` and `EncodingFlow`. From my reading of the CrewAI source (not a run), the same checkpoint failure applies, and you'd still need the Step 3 patch or a fixed CrewAI release. What it changes is where the memory records live: per the guide, they "survive redeploys" and are shared by every service using the same `user_id`.

From the guide:

**Unverified (not run):**

```bash
pip install crewai-memorysync
```

```python
from crewai import Crew
from crewai.memory import Memory
from crewai_memorysync import MemorySyncStorage

crew = Crew(
    agents=[researcher, writer],
    tasks=[research_task, write_task],
    memory=Memory(
        storage=MemorySyncStorage(user_id="support-crew"),
    ),
)
crew.kickoff()
```

The guide says it needs crewai 1.0+ and Python 3.10+, and `MEMORYSYNC_API_KEY` in the environment. *Snippet adapted from the [MemorySync CrewAI guide](https://docs.memorysync.io/guides/crewai?utm_source=hashnode&utm_medium=organic_page&utm_campaign=v5_oct25&utm_content=pseo_crewai-memory-checkpoint-unable-to-serialize-unknown-type-modelmetaclass); I haven't run it for this page.* For a longer walkthrough, see [Persistent memory for CrewAI 1.x](https://memorysync.hashnode.dev/crewai-persistent-memory-memorysync).

**Try it:** MemorySync is optional here: the steps above work without it. If you want the memory layer from the snippet, [see MemorySync](https://memorysync.io/?utm_source=hashnode&utm_medium=organic_page&utm_campaign=v5_oct25&utm_content=pseo_crewai-memory-checkpoint-unable-to-serialize-unknown-type-modelmetaclass), create an API key in the dashboard and drop the snippet into your agent.

## Troubleshooting

- **Still no checkpoint file with the patch loaded.** Make sure the import runs before the crew is built, in the same process. Then read the `CheckpointFailedEvent` error: if it no longer mentions `ModelMetaclass`, it's a different checkpoint problem.
- **`ImportError` on `_serialize_initial_state`.** Your CrewAI version has moved or renamed the private helper. Check whether that release already includes the fix (Step 4) before adapting the patch.
- **You call `state.checkpoint()` yourself.** The [checkpointing docs](https://docs.crewai.com/en/concepts/checkpointing) say manual `state.checkpoint()` and `state.acheckpoint()` calls "re-raise on failure", so with memory on you'd see this error raised instead of logged. I didn't run the manual path.
- **`Please provide an OpenAI API key` when memory starts.** That's a different error about the default embedder; see [CrewAI memory=True: "Please provide an OpenAI API key"](https://memorysync.hashnode.dev/crewai-memory-true-please-provide-an-openai-api-key).

## Common questions

**Did the crew's results or memory get lost?**
Not by this bug, in my runs: the tasks ran and the crew continued. What's missing is the checkpoint, so a crash or interruption can't resume from where it stopped.

**Is it only the `task_completed` trigger?**
The failure is in serializing the memory flows that are part of the run's state, so any automatic trigger that writes that state should hit it once a memory flow exists. I only tested the default `task_completed` trigger with the JSON provider.

**Does `depth="shallow"` avoid it?**
It avoids creating a `RecallFlow` for your own `recall()` calls, but agents use the default depth, and every save creates an `EncodingFlow`. I didn't test a shallow-only setup end to end.

**Will the patch break restoring old checkpoints?**
In my runs, checkpoints from the crew without memory restored the same with and without the patch, and a state written with the patch also loaded in a process without it. I didn't test checkpoints written by other CrewAI versions.

## Related pages

- [CrewAI memory=True: "Please provide an OpenAI API key" when you're not using OpenAI (embedder fix)](https://memorysync.hashnode.dev/crewai-memory-true-please-provide-an-openai-api-key)
- [Persistent memory for CrewAI 1.x: keep crew memory across runs and redeploys](https://memorysync.hashnode.dev/crewai-persistent-memory-memorysync)
- [LangGraph memory explained: checkpointer vs store, and per-user memory across threads](https://memorysync.hashnode.dev/langgraph-checkpointer-vs-store-cross-thread-memory)

## Sources

- github.com/crewAIInc/crewAI/issues/8318 (opened 2026-10-20, open, labeled bug, 0 comments; exact error, offline reproduction script, cause; read via the GitHub API 2026-10-31, status re-checked 2026-11-13)
- github.com/crewAIInc/crewAI/pull/8327 (opened 2026-10-20, open, not merged; diff for `recall_flow.py` and `encoding_flow.py` and the added round-trip test; read 2026-10-31, still open and not merged 2026-11-13)
- github.com/crewAIInc/crewAI/pull/5600 (base `Flow` `initial_state` serializer, referenced by the issue and the PR)
- PyPI: crewai 1.15.28 and 1.15.33 (release times; installed source of `crewai/memory/recall_flow.py`, `encoding_flow.py`, `unified_memory.py`, `flow/runtime/__init__.py`, `state/checkpoint_listener.py` and `events/types/checkpoint_events.py`; 2026-10-31); crewai 1.15.41 (release time; source of `crewai/memory/recall_flow.py`, `encoding_flow.py` and `flow/runtime/__init__.py`, read, not run; 2026-11-13)
- CrewAI docs: Checkpointing (fetched 2026-10-31); Memory concepts, docs.crewai.com/en/concepts/memory (fetched 2026-10-31)
- Offline runs (2026-10-31): the issue's reproduction on crewai 1.15.28 and 1.15.33 with and without the patch; the `CheckpointFailedEvent` listener on 1.15.33; a JSON dump-and-restore check of `RecallFlow` and `EncodingFlow` mirroring the PR's test; all with scripted model and embedding responses and no network
- MemorySync CrewAI guide: docs.memorysync.io/guides/crewai (fetched 2026-10-31)

## Try it

For an overview of MemorySync, see [the MemorySync homepage](https://memorysync.io/?utm_source=hashnode&utm_medium=organic_page&utm_campaign=v5_oct25&utm_content=pseo_crewai-memory-checkpoint-unable-to-serialize-unknown-type-modelmetaclass).

**Next step:** read the [MemorySync CrewAI guide](https://docs.memorysync.io/guides/crewai?utm_source=hashnode&utm_medium=organic_page&utm_campaign=v5_oct25&utm_content=pseo_crewai-memory-checkpoint-unable-to-serialize-unknown-type-modelmetaclass), then [see MemorySync](https://memorysync.io/?utm_source=hashnode&utm_medium=organic_page&utm_campaign=v5_oct25&utm_content=pseo_crewai-memory-checkpoint-unable-to-serialize-unknown-type-modelmetaclass). Founding offer: use code FOUNDING30 for 30% off Starter for your first 3 months (Settings → Billing).

