Skip to content

fix(backend/copilot): prevent title update from overwriting session messages - #12302

Merged
majdyz merged 2 commits into
devfrom
fix/copilot-session-overwrite-on-empty-result
Mar 5, 2026
Merged

fix(backend/copilot): prevent title update from overwriting session messages#12302
majdyz merged 2 commits into
devfrom
fix/copilot-session-overwrite-on-empty-result

Conversation

@majdyz

@majdyz majdyz commented Mar 5, 2026

Copy link
Copy Markdown
Contributor

Changes 🏗️

Fixes a race condition in update_session_title() where the background title generation task could overwrite the Redis session cache with a stale snapshot, causing the copilot to "forget" its previous turns.

Root cause: update_session_title() performs a read-modify-write on the Redis cache (read full session → set title → write back). Meanwhile, upsert_chat_session() writes a newer version with more messages during streaming. If the title task reads early (e.g., 34 messages) and writes late (after streaming persisted 101 messages), the stale 34-message version overwrites the 101-message version. When the next message lands on a different pod, it loads the stale session from Redis.

Fix: Replace the read-modify-write with a simple cache invalidation (invalidate_session_cache). The title is already updated in the DB; the next access just reloads from DB with the correct title and messages. No locks, no deserialization of the full session blob, no risk of stale overwrites.

Evidence from prod logs (session 41a3814c):

  • Pod tm2jb persisted session with 101 messages
  • Pod phflm loaded session from Redis cache with only 35 messages (66 messages lost)
  • The title background task ran between these events, overwriting the cache

Checklist 📋

For code changes:

  • I have clearly listed my changes in the PR description
  • I have made a test plan
  • I have tested my changes according to the test plan:
    • poetry run pytest backend/copilot/model_test.py — 15/15 pass
    • All pre-commit hooks pass (ruff, black, isort, pyright)
    • After deploy: verify long sessions no longer lose context on multi-pod setups

…essages

The background title generation task in update_session_title() performs a
read-modify-write on the Redis session cache (read session → set title →
write back).  This was not synchronized with upsert_chat_session() which
persists messages during streaming.

Race condition: if the title task reads the cache between streaming turns
(e.g., when the session has 34 messages), then writes it back after
streaming completes (when the session has 101 messages), the write
overwrites the 101-message version with the stale 34-message snapshot.
When the next user message lands on a different pod, it loads the stale
session from Redis and the model "forgets" everything that happened.

Fix: acquire the same _get_session_lock used by upsert_chat_session
before the read-modify-write in update_session_title.  Since the title
background task always runs on the same pod as the streaming function,
the in-process asyncio lock serializes the two operations correctly.
@majdyz
majdyz requested a review from a team as a code owner March 5, 2026 18:30
@majdyz
majdyz requested review from Bentlybro and Pwuts and removed request for a team March 5, 2026 18:30
@github-project-automation github-project-automation Bot moved this to 🆕 Needs initial review in AutoGPT development kanban Mar 5, 2026
@github-actions github-actions Bot added platform/backend AutoGPT Platform - Back end size/m labels Mar 5, 2026
@coderabbitai

coderabbitai Bot commented Mar 5, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 65017780-80d7-4f73-9fcd-784810f780cf

📥 Commits

Reviewing files that changed from the base of the PR and between 6a0bfbd and cf7f27d.

📒 Files selected for processing (1)
  • autogpt_platform/backend/backend/copilot/model.py
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: types
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.13)
  • GitHub Check: test (3.12)
  • GitHub Check: Seer Code Review
  • GitHub Check: Check PR Status
  • GitHub Check: Analyze (typescript)
🧰 Additional context used
📓 Path-based instructions (4)
autogpt_platform/backend/**/*.py

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

autogpt_platform/backend/**/*.py: Use Python 3.11 (required; managed by Poetry via pyproject.toml) for backend development
Always run 'poetry run format' (Black + isort) before linting in backend development
Always run 'poetry run lint' (ruff) after formatting in backend development

Files:

  • autogpt_platform/backend/backend/copilot/model.py
autogpt_platform/backend/**/*.{py,txt}

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

Use poetry run prefix for all Python commands, including testing, linting, formatting, and migrations

Files:

  • autogpt_platform/backend/backend/copilot/model.py
autogpt_platform/backend/backend/**/*.py

📄 CodeRabbit inference engine (autogpt_platform/backend/CLAUDE.md)

Use Prisma ORM for database operations in PostgreSQL with pgvector for embeddings

Files:

  • autogpt_platform/backend/backend/copilot/model.py
autogpt_platform/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Format Python code with poetry run format

Files:

  • autogpt_platform/backend/backend/copilot/model.py
🧠 Learnings (4)
📓 Common learnings
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12284
File: autogpt_platform/frontend/src/app/api/openapi.json:11897-11900
Timestamp: 2026-03-04T23:58:09.319Z
Learning: Repo: Significant-Gravitas/AutoGPT — PR `#12284`
Backend/frontend OpenAPI codegen convention: In backend/api/features/store/model.py, the StoreSubmission and StoreSubmissionAdminView models define submitted_at: datetime | None, changes_summary: str | None, and instructions: str | None with no default. This is intentional to produce “required but nullable” fields in OpenAPI (properties appear in required[] and use anyOf [type, null]). This matches Prisma’s submittedAt DateTime? and changesSummary String?. Do not flag this as a required/nullable mismatch.
📚 Learning: 2026-02-26T17:02:22.448Z
Learnt from: Pwuts
Repo: Significant-Gravitas/AutoGPT PR: 12211
File: .pre-commit-config.yaml:160-179
Timestamp: 2026-02-26T17:02:22.448Z
Learning: Keep the pre-commit hook pattern broad for autogpt_platform/backend to ensure OpenAPI schema changes are captured. Do not narrow to backend/api/ alone, since the generated schema depends on Pydantic models across multiple directories (backend/data/, backend/blocks/, backend/copilot/, backend/integrations/, backend/util/). Narrowing could miss schema changes and cause frontend type desynchronization.

Applied to files:

  • autogpt_platform/backend/backend/copilot/model.py
📚 Learning: 2026-03-04T08:04:35.881Z
Learnt from: majdyz
Repo: Significant-Gravitas/AutoGPT PR: 12273
File: autogpt_platform/backend/backend/copilot/tools/workspace_files.py:216-220
Timestamp: 2026-03-04T08:04:35.881Z
Learning: In the AutoGPT Copilot backend, ensure that SVG images are not treated as vision image types by excluding 'image/svg+xml' from INLINEABLE_MIME_TYPES and MULTIMODAL_TYPES in tool_adapter.py; the Claude API supports PNG, JPEG, GIF, and WebP for vision. SVGs (XML text) should be handled via the text path instead, not the vision path.

Applied to files:

  • autogpt_platform/backend/backend/copilot/model.py
📚 Learning: 2026-03-05T15:42:08.207Z
Learnt from: ntindle
Repo: Significant-Gravitas/AutoGPT PR: 12297
File: .claude/skills/backend-check/SKILL.md:14-16
Timestamp: 2026-03-05T15:42:08.207Z
Learning: In Python files under autogpt_platform/backend (recursively), rely on poetry run format to perform formatting (Black + isort) and linting (ruff). Do not run poetry run lint as a separate step after poetry run format, since format already includes linting checks.

Applied to files:

  • autogpt_platform/backend/backend/copilot/model.py
🔇 Additional comments (1)
autogpt_platform/backend/backend/copilot/model.py (1)

708-711: Good change: cache invalidation avoids stale session overwrite.

Replacing cache read-modify-write with invalidate_session_cache(session_id) removes the overwrite vector while still letting the updated title load from DB on next access.


Walkthrough

Replaced an in-place cached-session update in update_session_title with a cache invalidation call (invalidate_session_cache(session_id)), removing the previous read-modify-write cache mutation and its surrounding error handling so the cache will be reloaded from the DB on next access.

Changes

Cohort / File(s) Summary
Cache invalidation change
autogpt_platform/backend/backend/copilot/model.py
Replaced in-place cached session mutation and surrounding error handling in update_session_title with a call to invalidate_session_cache(session_id) after successful DB update; removed read-modify-write cache update logic.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • 0ubbe
  • Torantulino
  • kcze

Poem

🐰🌿 I nudged the cache and hopped away,
A DB-first bloom will greet the day.
No furtive writes to stale, shared ground,
Now fresh truth springs when next looked round.
🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main fix: preventing title updates from overwriting session messages, which is the core issue resolved in this PR.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, explaining the race condition, root cause, the fix implemented, and providing production evidence.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/copilot-session-overwrite-on-empty-result

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.

@github-actions

github-actions Bot commented Mar 5, 2026

Copy link
Copy Markdown
Contributor

🔍 PR Overlap Detection

This check compares your PR against all other open PRs targeting the same branch to detect potential merge conflicts early.

🔴 Merge Conflicts Detected

The following PRs have been tested and will have merge conflicts if merged after this PR. Consider coordinating with the authors.

Summary: 1 conflict(s), 0 medium risk, 0 low risk (out of 1 PRs with file overlap)


Auto-generated on push. Ignores: openapi.json, lock files.

Replace the lock + read + mutate + write pattern with a simple cache
invalidation. This eliminates the race condition more robustly — no stale
snapshot can ever overwrite fresh data.
@majdyz
majdyz requested a review from Torantulino March 5, 2026 18:42
@majdyz
majdyz enabled auto-merge March 5, 2026 18:42
@github-project-automation github-project-automation Bot moved this from 🆕 Needs initial review to 👍🏼 Mergeable in AutoGPT development kanban Mar 5, 2026
@majdyz
majdyz added this pull request to the merge queue Mar 5, 2026
Merged via the queue into dev with commit 21c705a Mar 5, 2026
22 checks passed
@majdyz
majdyz deleted the fix/copilot-session-overwrite-on-empty-result branch March 5, 2026 19:07
@github-project-automation github-project-automation Bot moved this from 👍🏼 Mergeable to ✅ Done in AutoGPT development kanban Mar 5, 2026
majdyz added a commit that referenced this pull request Mar 8, 2026
…essages (#12302)

### Changes 🏗️

Fixes a race condition in `update_session_title()` where the background
title generation task could overwrite the Redis session cache with a
stale snapshot, causing the copilot to "forget" its previous turns.

**Root cause:** `update_session_title()` performs a read-modify-write on
the Redis cache (read full session → set title → write back). Meanwhile,
`upsert_chat_session()` writes a newer version with more messages during
streaming. If the title task reads early (e.g., 34 messages) and writes
late (after streaming persisted 101 messages), the stale 34-message
version overwrites the 101-message version. When the next message lands
on a different pod, it loads the stale session from Redis.

**Fix:** Replace the read-modify-write with a simple cache invalidation
(`invalidate_session_cache`). The title is already updated in the DB;
the next access just reloads from DB with the correct title and
messages. No locks, no deserialization of the full session blob, no risk
of stale overwrites.

**Evidence from prod logs (session `41a3814c`):**
- Pod `tm2jb` persisted session with 101 messages
- Pod `phflm` loaded session from Redis cache with only 35 messages (66
messages lost)
- The title background task ran between these events, overwriting the
cache

### Checklist 📋

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
  - [x] `poetry run pytest backend/copilot/model_test.py` — 15/15 pass
  - [x] All pre-commit hooks pass (ruff, black, isort, pyright)
- [ ] After deploy: verify long sessions no longer lose context on
multi-pod setups
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform/backend AutoGPT Platform - Back end size/m

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

2 participants