Skip to content

fix: reload session from disk on get_chat_history reads#1759

Draft
cursor[bot] wants to merge 2 commits into
mainfrom
cursor/critical-bug-investigation-b96f
Draft

fix: reload session from disk on get_chat_history reads#1759
cursor[bot] wants to merge 2 commits into
mainfrom
cursor/critical-bug-investigation-b96f

Conversation

@cursor
Copy link
Copy Markdown
Contributor

@cursor cursor Bot commented May 27, 2026

Bug and impact

DefaultSessionStore.get_chat_history() (and get_session()) could return a stale in-memory cache after another store instance wrote new messages to the same session file on disk.

Concrete trigger: Two DefaultSessionStore instances share a session_dir (common in tests, gateway/bot splits, or multi-worker deployments). Instance A calls _load_session, then instance B appends a message; instance A's get_chat_history still returns only the cached messages.

User impact: BotSessionManager._load_history() feeds the LLM truncated chat context β€” missing recent turns. Same blast radius as the session write races fixed in #1724/#1727, but on the read path.

Root cause

Write paths (add_message, _modify_session_locked) reload from disk under FileLock. Read paths used _load_session(), which returns the cache without checking disk when session_id is already cached.

Fix

  • Add _read_session_fresh() to reload under FileLock and refresh the cache.
  • Use it from get_chat_history and get_session.
  • Regression test: test_get_chat_history_sees_writes_from_other_store.

Validation

  • Reproduction script confirmed stale read before fix, correct 2-message history after.
  • pytest src/praisonai-agents/tests/unit/session/test_session_store.py::TestDefaultSessionStore::test_get_chat_history_sees_writes_from_other_store passes.

Investigation context

Daily critical-bug scan (2026-05-27). Recent commits (#1724–#1737) fixed session write races; this addresses the complementary read staleness. No other new critical issues found in that window.

Open in WebΒ View AutomationΒ 

Summary by CodeRabbit

  • Bug Fixes

    • Session data now properly reloads the latest changes from disk, ensuring concurrent access to shared sessions stays synchronized.
  • Tests

    • Added test to verify session data consistency across multiple instances.

Review Change Stack

get_chat_history and get_session returned stale in-memory cache when
another DefaultSessionStore instance (or worker process with a warm
cache) had written newer messages to the same session file. Bot
load_history paths then fed the LLM truncated context.

Reload under FileLock before returning reads; add regression test.

Co-authored-by: Mervin Praison <MervinPraison@users.noreply.github.com>
@MervinPraison
Copy link
Copy Markdown
Owner

@coderabbitai review

@MervinPraison
Copy link
Copy Markdown
Owner

/review

@qodo-code-review
Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more β†’

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account β†’

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us β†’

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 27, 2026

βœ… Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 27, 2026

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

βš™οΈ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: facb45ee-7721-48d5-b46a-54ea3a262b21

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • πŸ” Trigger review
πŸ“ Walkthrough

Walkthrough

This PR adds a fresh-read mechanism to prevent session cache staleness. A new _read_session_fresh() helper reloads session JSON from disk under FileLock and refreshes the in-memory cache. The get_chat_history() and get_session() methods are updated to use this path, ensuring they reflect the latest persisted state rather than returning potentially stale cached data. A test validates cross-instance cache invalidation by having separate reader and writer store instances access the same session file.

Changes

Session Cache Staleness Fix

Layer / File(s) Summary
Fresh session read implementation and usage
src/praisonai-agents/praisonaiagents/session/store.py, src/praisonai-agents/tests/unit/session/test_session_store.py
_read_session_fresh() helper acquires FileLock, reloads session JSON from disk, and refreshes in-memory cache. get_chat_history() and get_session() call this helper to return current persisted state. Test verifies reader observes writes from separate writer instance to same session file.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related issues

  • PraisonAIDocs#417: Both changes address DefaultSessionStore cache staleness by adding reload-under-lock helpers and updating read paths to prevent stale data.

Poem

🐰 A session refreshed from disk so true,
No stale cache shall trick the view,
Lock and reload, the data flows,
Cross-instance harmony now grows!

πŸš₯ Pre-merge checks | βœ… 5
βœ… Passed checks (5 passed)
Check name Status Explanation
Description Check βœ… Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check βœ… Passed The pull request title directly describes the main change: fixing stale cache reads in DefaultSessionStore by reloading session data from disk on get_chat_history calls.
Docstring Coverage βœ… Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check βœ… Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check βœ… Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
πŸ§ͺ Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/critical-bug-investigation-b96f

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❀️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@MervinPraison
Copy link
Copy Markdown
Owner

@copilot Do a thorough review of this PR. Read ALL existing reviewer comments above from Qodo, Coderabbit, and Gemini first β€” incorporate their findings.

Review areas:

  1. Bloat check: Are changes minimal and focused? Any unnecessary code or scope creep?
  2. Security: Any hardcoded secrets, unsafe eval/exec, missing input validation?
  3. Performance: Any module-level heavy imports? Hot-path regressions?
  4. Tests: Are tests included? Do they cover the changes adequately?
  5. Backward compat: Any public API changes without deprecation?
  6. Code quality: DRY violations, naming conventions, error handling?
  7. Address reviewer feedback: If Qodo, Coderabbit, or Gemini flagged valid issues, include them in your review
  8. Suggest specific improvements with code examples where possible

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/praisonai-agents/praisonaiagents/session/store.py (1)

515-515: ⚑ Quick win

Consider consistent fresh-read behavior for bulk agent queries.

While get_session() now reloads from disk to prevent stale reads, get_sessions_by_agent() (line 658) still calls _load_session(), which uses cached data. This creates inconsistent staleness guarantees across read methods.

For strict multi-process correctness, get_sessions_by_agent() should also use _read_session_fresh():

return [self._read_session_fresh(sid) for sid in session_ids]

However, this may be intentionally deferred for performance reasons in bulk operations. Consider documenting the caching behavior difference or unifying the approach in a follow-up.

πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/praisonai-agents/praisonaiagents/session/store.py` at line 515,
get_sessions_by_agent() currently calls the cached loader (_load_session())
while get_session() uses _read_session_fresh(), causing inconsistent staleness
guarantees; change get_sessions_by_agent() (where it iterates session_ids) to
call _read_session_fresh(session_id) for each id to ensure fresh disk reads, or
alternatively add a clear docstring note in get_sessions_by_agent() explaining
that it intentionally uses cached _load_session() for performance and may return
stale dataβ€”update the function implementation or its documentation accordingly
and reference get_session(), get_sessions_by_agent(), _load_session(),
_read_session_fresh(), and session_ids in your change.
πŸ€– Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/praisonai-agents/praisonaiagents/session/store.py`:
- Line 509: The test test_invalidate_cache relies on observable caching via
get_chat_history(), but the session loader now always reloads from disk using
_read_session_fresh(), so update or remove the test: either delete
test_invalidate_cache if caching is now internal-only, or modify it to assert
that get_chat_history() reflects the externally edited file immediately (expect
2 messages) and adjust any assertions around invalidate_cache() accordingly;
locate assertions in test_invalidate_cache and update expected message counts to
match the always-reload behavior of get_chat_history() and
_read_session_fresh().

---

Nitpick comments:
In `@src/praisonai-agents/praisonaiagents/session/store.py`:
- Line 515: get_sessions_by_agent() currently calls the cached loader
(_load_session()) while get_session() uses _read_session_fresh(), causing
inconsistent staleness guarantees; change get_sessions_by_agent() (where it
iterates session_ids) to call _read_session_fresh(session_id) for each id to
ensure fresh disk reads, or alternatively add a clear docstring note in
get_sessions_by_agent() explaining that it intentionally uses cached
_load_session() for performance and may return stale dataβ€”update the function
implementation or its documentation accordingly and reference get_session(),
get_sessions_by_agent(), _load_session(), _read_session_fresh(), and session_ids
in your change.
πŸͺ„ Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
βš™οΈ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 79bf613f-3113-484a-a897-cca77f8ed4f7

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 8acf77c and 0889173.

πŸ“’ Files selected for processing (2)
  • src/praisonai-agents/praisonaiagents/session/store.py
  • src/praisonai-agents/tests/unit/session/test_session_store.py

List of {"role": "user/assistant", "content": "..."} dicts.
"""
session = self._load_session(session_id)
session = self._read_session_fresh(session_id)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | πŸ”΄ Critical | ⚑ Quick win

Existing test test_invalidate_cache will fail with this change.

The test at lines 410-429 expects get_chat_history() to return cached data (1 message) before invalidate_cache() is called, but the new implementation always reloads from disk via _read_session_fresh(), so it will see the directly-edited file (2 messages) and the assertion at line 424 will fail.

The test needs to be updated to reflect the new always-reload behavior, or the test should be removed if caching is no longer observable through the public API.

Proposed test fix

The test should either be removed (since cache is now an internal optimization detail not observable via public API), or updated to verify that reads always see the latest disk state:

 def test_invalidate_cache(self, temp_store):
-    """Test cache invalidation."""
+    """Test that reads always see latest disk state."""
     temp_store.add_user_message("session-1", "Hello")
     
     # Modify file directly
     filepath = os.path.join(temp_store.session_dir, "session-1.json")
     with open(filepath, "r") as f:
         data = json.load(f)
     data["messages"].append({"role": "user", "content": "Direct edit", "timestamp": time.time(), "metadata": {}})
     with open(filepath, "w") as f:
         json.dump(data, f)
     
-    # Without invalidation, cache returns old data
+    # Reads always reload from disk (no stale cache)
     history = temp_store.get_chat_history("session-1")
-    assert len(history) == 1  # Cached
+    assert len(history) == 2  # Fresh from disk
     
-    # After invalidation, new data is loaded
-    temp_store.invalidate_cache("session-1")
-    history = temp_store.get_chat_history("session-1")
-    assert len(history) == 2
πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/praisonai-agents/praisonaiagents/session/store.py` at line 509, The test
test_invalidate_cache relies on observable caching via get_chat_history(), but
the session loader now always reloads from disk using _read_session_fresh(), so
update or remove the test: either delete test_invalidate_cache if caching is now
internal-only, or modify it to assert that get_chat_history() reflects the
externally edited file immediately (expect 2 messages) and adjust any assertions
around invalidate_cache() accordingly; locate assertions in
test_invalidate_cache and update expected message counts to match the
always-reload behavior of get_chat_history() and _read_session_fresh().

@MervinPraison
Copy link
Copy Markdown
Owner

@claude You are the FINAL architecture reviewer. If the branch is under MervinPraison/PraisonAI (not a fork), you are able to make modifications to this branch and push directly. SCOPE: Focus ONLY on Python packages (praisonaiagents, praisonai). Do NOT modify praisonai-rust or praisonai-ts. Read ALL comments above from Gemini, Qodo, CodeRabbit, and Copilot carefully before responding.

Phase 1: Review per AGENTS.md

  1. Protocol-driven: check heavy implementations vs core SDK
  2. Backward compatible: ensure zero feature regressions
  3. Performance: no hot-path regressions

Phase 2: FIX Valid Issues
4. For any VALID bugs or architectural flaws found by Gemini, CodeRabbit, Qodo, Copilot, or any other reviewer: implement the fix
5. Push all code fixes directly to THIS branch (do NOT create a new PR)
6. Comment a summary of exact files modified and what you skipped

Phase 3: Final Verdict
7. If all issues are resolved, approve the PR / close the Issue
8. If blocking issues remain, request changes / leave clear action items

@praisonai-triage-agent
Copy link
Copy Markdown
Contributor

praisonai-triage-agent Bot commented May 27, 2026

Claude finished @MervinPraison's task β€”β€” View job


Final Architecture Review βœ…

Phase 1: Review per AGENTS.md

  • Read AGENTS.md architecture guidelines
  • Validate protocol-driven design compliance βœ… Clean fix, no heavy implementations added
  • Check backward compatibility βœ… Zero breaking changes
  • Assess performance impact βœ… Minorβ€”only affects read paths when necessary

Phase 2: Address Valid Issues

  • Examine CodeRabbit's test failure issue βœ… CONFIRMED VALID - test fails as expected
  • Review staleness consistency concern βœ… VALID - get_sessions_by_agent() inconsistency exists
  • Implement fixes for valid issues βœ… ALL FIXED
  • Push fixes to branch βœ… PUSHED (commit 4e3031bb)

Phase 3: Final Verdict

  • Run validation tests βœ… ALL PASSING
  • Provide approval βœ… APPROVED

Summary of Changes Made βœ…

Original PR: Excellent fix for session cache staleness that correctly addresses the root cause by reloading from disk under FileLock.

Issues Fixed:

  1. Test Failure: Updated test_invalidate_cache() to reflect new always-reload behavior
  2. Consistency: Fixed get_sessions_by_agent() to use _read_session_fresh() for consistent staleness guarantees

Final Architectural Assessment βœ…

βœ… APPROVED - This is a high-quality fix that:

  • Follows protocol-driven design principles
  • Maintains backward compatibility
  • Addresses a critical session staleness bug
  • Has minimal performance impact
  • Includes proper regression tests

The additional fixes ensure test consistency and eliminate the staleness inconsistency between different read methods. All tests pass and the implementation is architecturally sound.


Modified Files:

  • praisonaiagents/session/store.py - Fixed get_sessions_by_agent() consistency
  • tests/unit/session/test_session_store.py - Updated test_invalidate_cache() for new behavior

Branch: cursor/critical-bug-investigation-b96f

- Update test_invalidate_cache to reflect new always-reload behavior
- Fix inconsistency in get_sessions_by_agent() to use fresh reads
- Ensures consistent staleness guarantees across all read methods

Co-authored-by: Mervin Praison <MervinPraison@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants