Skip to content

fix(memory): reject an existing branch id in create_branch_from_turn - #4179

Closed
aryanputta wants to merge 1 commit into
openai:mainfrom
aryanputta:fix/branch-id-collision
Closed

fix(memory): reject an existing branch id in create_branch_from_turn#4179
aryanputta wants to merge 1 commit into
openai:mainfrom
aryanputta:fix/branch-id-collision

Conversation

@aryanputta

Copy link
Copy Markdown

Closes #4150.

Problem

create_branch_from_turn() passes branch_name straight through to _copy_messages_to_new_branch(), which unconditionally inserts message_structure rows tagged with that id. Passing the id of a branch that already exists merges the copied turns into it and then switches to it. There is no error, and afterwards the two histories cannot be told apart.

Change

Check the id before copying and raise ValueError. The method's docstring already documents ValueError as its failure mode, so this uses the existing contract rather than introducing a new one. A generated name is unaffected, since it embeds a timestamp.

Verification

  • Test added to tests/extensions/memory/test_advanced_sqlite_session.py. It asserts both halves: that the second call raises, and that branch message counts are unchanged afterwards, so a rejected call cannot have partially written.
  • Confirmed the test fails without the source change (1 failed, 67 passed) and passes with it (68 passed).
  • The assertion matches on "already exists". An earlier version of this test passed for the wrong reason: after the first call the session is on the new branch, where the requested turn no longer resolves, so turn validation raised ValueError on its own. Switching back to main first isolates the duplicate-id condition.
  • ruff check and ruff format --check clean on both files.

create_branch_from_turn() passed branch_name straight to
_copy_messages_to_new_branch(), which unconditionally inserts
message_structure rows tagged with that id. Passing the id of a branch
that already exists therefore merged the copied turns into it and then
switched to it, with no error and no way to tell the two histories apart
afterwards.

Check the id first and raise ValueError, which the docstring already
documents as the failure mode for this method. A generated name is
unaffected, since it embeds a timestamp.

Closes openai#4150

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2602075824

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

if branch_name is None:
timestamp = int(time.time())
branch_name = f"branch_from_turn_{turn_number}_{timestamp}"
elif await self._branch_exists(branch_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.

P2 Badge Make the duplicate-branch check atomic

When two coroutines call create_branch_from_turn(..., "same") concurrently from a branch where the turn resolves, this check runs in its own locked DB operation and then releases the lock before _copy_messages_to_new_branch() inserts anything, so both calls can observe that the branch is absent and then both copy rows into the same branch. That leaves the merged/duplicated branch state this change is meant to reject; the existence check needs to be performed in the same locked transaction as the copy, or enforced by a database-level uniqueness boundary for branch creation. .agents/references/session-persistence.mdL19-L20

Useful? React with 👍 / 👎.

@seratch seratch added duplicate This issue or pull request already exists feature:sessions labels Aug 4, 2026
@seratch

seratch commented Aug 5, 2026

Copy link
Copy Markdown
Member

Thanks for trying to fix this. The issue was resolved by #4186

@seratch seratch closed this Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

duplicate This issue or pull request already exists feature:sessions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AdvancedSQLiteSession.create_branch_from_turn silently merges into an existing branch

2 participants