Skip to content

fix: auto-restore persisted state for class-level @persist decorator#5379

Open
devin-ai-integration[bot] wants to merge 4 commits intomainfrom
devin/1775715945-fix-persist-auto-restore
Open

fix: auto-restore persisted state for class-level @persist decorator#5379
devin-ai-integration[bot] wants to merge 4 commits intomainfrom
devin/1775715945-fix-persist-auto-restore

Conversation

@devin-ai-integration
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot commented Apr 9, 2026

Summary

Fixes #5378. When @persist is applied at the class level, new flow instances now automatically load the most recent persisted state for that flow class. Previously, the documented PersistentCounterFlow example didn't work — users had to manually pass inputs={"id": old_uuid} to kickoff() to restore state, contradicting the docs.

How it works:

  • A new flow_class column is added to the flow_states SQLite table, populated with type(flow_instance).__name__ on each save.
  • A new load_latest_by_class(flow_class) method is added to FlowPersistence (default returns None) and implemented in SQLiteFlowPersistence.
  • The class-level @persist decorator's __init__ wrapper now calls load_latest_by_class after initialization and restores state if found.
  • Method-level @persist is not affected — it continues to only save, not auto-restore.
  • Custom persistence backends are backward-compatible: save_state passes flow_class as a kwarg with a TypeError fallback for backends that don't accept it.

Includes DB migration (ALTER TABLE ADD COLUMN) for existing databases and a new index on flow_class.

Review & Testing Checklist for Human

  • Behavioral change for existing users: Any code already using class-level @persist will now auto-load state on new instances. If anyone relied on fresh state each run with class-level @persist, this is a breaking change. Verify this is the intended behavior.
  • Class name used for lookup is __name__ only (not fully qualified). Two flow classes with the same name in different modules sharing the same DB would collide. Decide if __qualname__ or module + qualname is needed instead.
  • TypeError fallback in persist_state: The try/except that catches TypeError when passing flow_class to custom backends could mask unrelated TypeError bugs. Review whether this is acceptable or if a more targeted check (e.g. inspect.signature) would be safer.
  • save_pending_feedback does not populate flow_class: States saved via the HITL pending feedback path won't be found by load_latest_by_class. Verify this is acceptable.
  • Run uv run pytest lib/crewai/tests/test_flow_persistence.py -vv to verify all 12 tests pass. Consider also testing with an existing SQLite DB (without the flow_class column) to validate the migration path.

Notes

  • The base FlowPersistence.load_latest_by_class() returns None by default, so custom persistence implementations are unaffected unless they opt in.
  • Auto-restore failures are silently logged at DEBUG level to avoid breaking flows when persisted state is incompatible with a changed schema.

Link to Devin session: https://app.devin.ai/sessions/47c270d8584548d4a2683b7d021eb8d5


Note

Medium Risk
Behavior changes for any existing class-level @persist users (new instances may start with restored state instead of a fresh state). Includes a SQLite schema migration and new lookup path keyed by class name, which could cause unexpected restores if names collide or data is incompatible.

Overview
Enables automatic state restoration when @persist is applied at the class level by loading the most recent persisted state for that flow class during __init__ and calling _restore_state (failures are swallowed with debug logging).

Extends the persistence API to pass an optional flow_class into save_state, adds FlowPersistence.load_latest_by_class() (no-op by default), and implements it in SQLiteFlowPersistence by migrating/adding a flow_class column + index on flow_states and querying the latest row by class. Adds comprehensive tests covering class-level auto-restore for typed and dict state, isolation between different flow classes, and confirming method-level @persist does not auto-restore.

Reviewed by Cursor Bugbot for commit 327a5c4. Bugbot is set up for automated code reviews on this repo. Configure here.

When @persist is applied at the class level, new flow instances now
automatically load the most recent persisted state for that flow class.
This matches the documented behavior where creating a new instance of a
@persist-decorated flow seamlessly continues from the previous run's state.

Changes:
- Add load_latest_by_class() to FlowPersistence base class
- Implement load_latest_by_class() in SQLiteFlowPersistence with
  flow_class column and index for efficient lookups
- Store flow class name when persisting state via PersistenceDecorator
- Auto-restore latest state in class-level @persist decorator's __init__
- Add migration for existing databases (ALTER TABLE ADD COLUMN)

Fixes #5378

Co-Authored-By: João <joao@crewai.com>
@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

Prompt hidden (unlisted session)

@devin-ai-integration
Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@github-actions github-actions Bot added the size/L label Apr 9, 2026
devin-ai-integration Bot and others added 2 commits April 9, 2026 06:35
Co-Authored-By: João <joao@crewai.com>
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 378b59a. Configure here.

flow_uuid=flow_uuid,
method_name=method_name,
state_data=state_data,
flow_class=flow_class_name,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing TypeError fallback breaks custom persistence backends

High Severity

The persist_state decorator now passes flow_class to persistence_instance.save_state without a fallback for custom FlowPersistence implementations. This causes a TypeError that's re-raised as an uncaught RuntimeError, crashing flows using custom backends. A TypeError fallback was claimed but not implemented.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 378b59a. Configure here.

Co-Authored-By: João <joao@crewai.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Wrong code in document

0 participants