Description
When persisting Flow state via @persist or SQLiteFlowPersistence, workflows with structured Pydantic state models containing standard fields like datetime, UUID, set, Decimal, or Path crash during state saving with:
RuntimeError: State persistence failed: Object of type datetime is not JSON serializable
Root Cause:
In lib/crewai/src/crewai/flow/persistence/sqlite.py:
_to_state_dict calls state_data.model_dump() without specifying mode="json". In Pydantic v2, model_dump() keeps native Python types (datetime.datetime, uuid.UUID, set, etc.) instead of converting them to JSON primitives.
- In
_save_state_sql (line 142) and save_pending_feedback (line 241), json.dumps(state_dict) is called directly without a serializer fallback (default=str), which immediately raises a TypeError on any non-primitive type.
Steps to Reproduce
- Define a Flow with a Pydantic state model containing a
datetime (or uuid.UUID, set).
- Attach
SQLiteFlowPersistence using @persist on a flow step.
- Call
flow.kickoff().
- Observe the flow crashing upon completing the persisted method.
Expected behavior
SQLiteFlowPersistence should serialize Pydantic state models in JSON mode (model_dump(mode="json")) and handle fallback dicts gracefully with default=str. When loaded back via load_state, Pydantic's model_validate restores them to their native types (datetime, UUID, set) without data loss.
Screenshots/Code snippets
from datetime import datetime, timezone
import uuid
from pydantic import BaseModel, Field
from crewai.flow.flow import Flow, start
from crewai.flow.persistence import persist, SQLiteFlowPersistence
class WorkflowState(BaseModel):
id: str = Field(default_factory=lambda: str(uuid.uuid4()))
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
user_id: uuid.UUID = Field(default_factory=uuid.uuid4)
tags: set[str] = Field(default_factory=lambda: {"analytics", "v1"})
persistence = SQLiteFlowPersistence("test_flow.db")
class SampleFlow(Flow[WorkflowState]):
initial_state = WorkflowState
@start()
@persist(persistence)
def step_one(self):
return "done"
flow = SampleFlow(persistence=persistence)
flow.kickoff()
Operating System
macOS Sonoma
Python Version
3.12
crewAI Version
1.15.20
crewAI Tools Version
1.15.20
Virtual Environment
Venv
Evidence
Failed to persist state for method step_one: Object of type datetime is not JSON serializable
Flow Method Failed
- Method: step_one
- Status: Failed
FLOW CRASHED: RuntimeError: State persistence failed: Object of type datetime is not JSON serializable
Possible Solution
- In
lib/crewai/src/crewai/flow/persistence/sqlite.py: update _to_state_dict to use state_data.model_dump(mode="json"):
@staticmethod
def _to_state_dict(state_data: dict[str, Any] | BaseModel) -> dict[str, Any]:
"""Convert state_data to a plain dict."""
if isinstance(state_data, BaseModel):
return state_data.model_dump(mode="json")
if isinstance(state_data, dict):
return state_data
raise ValueError(
f"state_data must be either a Pydantic BaseModel or dict, got {type(state_data)}"
)
### Additional context
I have tested the fix locally along with full roundtrip serialization/deserialization verification and unit tests. Ready to submit a PR!
Description
When persisting Flow state via
@persistorSQLiteFlowPersistence, workflows with structured Pydantic state models containing standard fields likedatetime,UUID,set,Decimal, orPathcrash during state saving with:RuntimeError: State persistence failed: Object of type datetime is not JSON serializableRoot Cause:
In
lib/crewai/src/crewai/flow/persistence/sqlite.py:_to_state_dictcallsstate_data.model_dump()without specifyingmode="json". In Pydantic v2,model_dump()keeps native Python types (datetime.datetime,uuid.UUID,set, etc.) instead of converting them to JSON primitives._save_state_sql(line 142) andsave_pending_feedback(line 241),json.dumps(state_dict)is called directly without a serializer fallback (default=str), which immediately raises aTypeErroron any non-primitive type.Steps to Reproduce
datetime(oruuid.UUID,set).SQLiteFlowPersistenceusing@persiston a flow step.flow.kickoff().Expected behavior
SQLiteFlowPersistenceshould serialize Pydantic state models in JSON mode (model_dump(mode="json")) and handle fallback dicts gracefully withdefault=str. When loaded back viaload_state, Pydantic'smodel_validaterestores them to their native types (datetime,UUID,set) without data loss.Screenshots/Code snippets
from datetime import datetime, timezone
import uuid
from pydantic import BaseModel, Field
from crewai.flow.flow import Flow, start
from crewai.flow.persistence import persist, SQLiteFlowPersistence
class WorkflowState(BaseModel):
id: str = Field(default_factory=lambda: str(uuid.uuid4()))
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
user_id: uuid.UUID = Field(default_factory=uuid.uuid4)
tags: set[str] = Field(default_factory=lambda: {"analytics", "v1"})
persistence = SQLiteFlowPersistence("test_flow.db")
class SampleFlow(Flow[WorkflowState]):
initial_state = WorkflowState
flow = SampleFlow(persistence=persistence)
flow.kickoff()
Operating System
macOS Sonoma
Python Version
3.12
crewAI Version
1.15.20
crewAI Tools Version
1.15.20
Virtual Environment
Venv
Evidence
Failed to persist state for method step_one: Object of type datetime is not JSON serializable
Flow Method Failed
FLOW CRASHED: RuntimeError: State persistence failed: Object of type datetime is not JSON serializable
Possible Solution
lib/crewai/src/crewai/flow/persistence/sqlite.py: update_to_state_dictto usestate_data.model_dump(mode="json"):