crewAIInc / crewAIInc/crewAI

PersistenceDecorator.persist_state raises wrong error message when Flow state is None

Open Beginner friendly
#7,220 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
58.8k
Forks
8.5k
Avg merge
1d 15h
Merged PRs (30d)
109

Description

PersistenceDecorator.persist_state in lib/crewai/src/crewai/flow/persistence/decorators.py raises the wrong error message when a Flow instance has no state at all, because the two ValueErrors it can raise internally are mapped to the wrong except clause.

The method has this structure (lines 94-144):

try:
    state = getattr(flow_instance, "state", None)
    if state is None:
        raise ValueError("Flow instance has no state")

    flow_uuid: str | None = None
    if isinstance(state, dict):
        flow_uuid = state.get("id")
    elif hasattr(state, "_unwrap"):
        ...
    elif isinstance(state, BaseModel) or hasattr(state, "id"):
        flow_uuid = getattr(state, "id", None)

    if not flow_uuid:
        raise ValueError("Flow state must have an 'id' field for persistence")

    ...
    try:
        ...
        persistence_instance.save_state(...)
    except Exception as e:
        ...
        raise RuntimeError(f"State persistence failed: {e!s}") from e
except AttributeError as e:
    error_msg = LOG_MESSAGES["state_missing"]
    ...
    raise ValueError(error_msg) from e
except (TypeError, ValueError) as e:
    error_msg = LOG_MESSAGES["id_missing"]
    ...
    raise ValueError(error_msg) from e

LOG_MESSAGES defines:

LOG_MESSAGES: Final[dict[str, str]] = {
    "save_state": "Saving flow state to memory for ID: {}",
    "save_error": "Failed to persist state for method {}: {}",
    "state_missing": "Flow instance has no state",
    "id_missing": "Flow state must have an 'id' field for persistence",
}

The "no state" case is raised as a ValueError at line 97 (raise ValueError("Flow instance has no state")), but ValueError is only caught by the second except clause, except (TypeError, ValueError), whose handler always uses LOG_MESSAGES["id_missing"]. The first except clause, except AttributeError, is the one associated with LOG_MESSAGES["state_missing"], but nothing in the try block ever raises a plain AttributeError for the "no state" condition (getattr(flow_instance, "state", None) cannot raise AttributeError since it passes a default).

So the two error paths are swapped: whenever a Flow instance has state is None, the exception that reaches the caller (and gets logged/printed) is:

ValueError: Flow state must have an 'id' field for persistence

instead of the correct:

ValueError: Flow instance has no state

This is misleading during debugging: someone whose Flow simply has no state attribute set will be told to check for a missing 'id' field, which is not the actual problem.

Repro:

from types import SimpleNamespace
from crewai.flow.persistence.decorators import PersistenceDecorator
from crewai.flow.persistence.sqlite import SQLiteFlowPersistence

flow_instance = SimpleNamespace(state=None)
PersistenceDecorator.persist_state(flow_instance, "some_method", SQLiteFlowPersistence())

Expected: ValueError: Flow instance has no state
Actual: ValueError: Flow state must have an 'id' field for persistence

Fix direction: swap the message assignment so the ValueError raised at line 97 maps to state_missing and the one at line 112 maps to id_missing (e.g. by raising distinct exception types, or by inspecting str(e) / using a single except clause that checks which condition triggered it, rather than relying on exception-type dispatch across two ValueError raise sites).

Duplicate-checked: searched repo:crewAIInc/crewAI is:issue persist_state ValueError, PersistenceDecorator, persist error message, and flow persistence id field — no existing issue describes this.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Read persist_state in lib/crewai/src/crewai/flow/persistence/decorators.py, focusing on lines 94-144 and its two ValueError paths. Run the provided SimpleNamespace and SQLiteFlowPersistence reproduction, then verify that a missing state reports "Flow instance has no state" while a missing id reports "Flow state must have an 'id' field for persistence".

Written by the indexing model from the issue text.

Assessment

Tech stack
python, sqlite
Domain
backend, databases
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
84/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.