Skip to content

fix(agent): auto-deduplicate session titles on collision - #26389

Closed
02356abc wants to merge 2 commits into
NousResearch:mainfrom
02356abc:fix/auto-title-dedup
Closed

fix(agent): auto-deduplicate session titles on collision#26389
02356abc wants to merge 2 commits into
NousResearch:mainfrom
02356abc:fix/auto-title-dedup

Conversation

@02356abc

@02356abc 02356abc commented May 15, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes a bug where auto-generated session titles silently failed when the
LLM produced a title already in use by another session, leaving the new
session untitled.

Related Issue

No existing issue found. This is a standalone bug fix.

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • `agent/title_generator.py`: In `auto_title_session`, catch the
    `ValueError` from `set_session_title` when the title collides, and
    fall back to `get_next_title_in_lineage()` which appends a `#N`
    suffix (e.g. "Hermes Agent Intro Support passing morph snapshot id #2").

Bug Report (Reproduction)

Observed behavior:

  1. Start session A with "Who are you?" → auto-title: "Hermes Agent Intro"
  2. Start session B with "Who are you?" → title generation succeeds but
    `set_session_title` raises `ValueError: Title already in use`
  3. Session B remains untitled (title = null)

Root cause: `SessionDB` enforces a uniqueness constraint on session
titles (`idx_sessions_title_unique`). The auto-title generator did not
handle collisions, so repeated greetings or identical first exchanges
would silently leave sessions untitled.

Analysis: The codebase already has infrastructure for handling
duplicate titles:

  • `resolve_session_by_title()` searches for "title #N" variants and
    returns the latest one
  • `get_next_title_in_lineage()` computes the next available `#N` suffix

However, the auto-title path (`auto_title_session`) was not using this
infrastructure — it simply gave up when `set_session_title` failed.

Fix: When a collision is detected, automatically append a `#N`
suffix using the existing `get_next_title_in_lineage()` helper. This
aligns the auto-title behaviour with `resolve_session_by_title()` and
makes the title-unique constraint workable instead of silently broken.

How to Test

  1. Start two sessions with the same first message (e.g. "Who are you?")
  2. Both sessions should receive auto-generated titles
  3. The second session's title should have a "Support passing morph snapshot id #2" suffix

Checklist

  • pytest tests/agent/test_title_generator.py -v (20 passed)
  • My commit messages follow Conventional Commits
  • My PR contains only changes related to this fix

@daimon-nous daimon-nous Bot added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint labels May 15, 2026
02356abc and others added 2 commits May 16, 2026 14:45
The auto-title generator silently failed when the LLM produced a title
that was already in use by another session, because SessionDB enforces
a uniqueness constraint on session titles.

Example:
- User starts session A with \"Who are you?\" → title \"Hermes Agent Intro\"
- User starts session B with \"Who are you?\" → title generation succeeds
  but set_session_title raises ValueError → session B remains untitled

Fix: catch the ValueError in auto_title_session and fall back to
get_next_title_in_lineage(), which appends a \"#N\" suffix (e.g.
\"Hermes Agent Intro NousResearch#2\"). This aligns with the existing
resolve_session_by_title() behaviour that already searches for
numbered variants.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…h#26389

Adds AUTHOR_MAP entry so the check-attribution CI step recognises the
author of this PR. The noreply address has no numeric prefix and thus
is not auto-resolved by the workflow's GitHub-noreply pattern.
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for targeting a real current-main failure: auto_title_session() currently swallows the ValueError from set_session_title() at agent/title_generator.py:154-163, while SessionDB rejects duplicate titles at hermes_state.py:2717-2746.

Problems

  • The added fallback is a one-shot read-then-write operation. get_next_title_in_lineage() reads variants at hermes_state.py:2865-2885, then set_session_title() performs a separate uniqueness-enforced write. Concurrent title workers can both choose the same #N; the losing retry remains untitled because the new nested handler only logs the second failure.
  • The diff adds no regression test. Current tests cover only the ordinary success/callback path at tests/agent/test_title_generator.py:228-249. Related PR fix(title): use lineage fallback for generated titles #26471 also identified this duplicate and contains focused fallback tests.

Suggested changes

  • Allocate/persist the numbered title atomically in SessionDB, or retry collisions safely until persistence succeeds.
  • Add coverage for initial collision, fallback success and callback value, plus a competing fallback collision.

Automated hermes-sweeper review.

@teknium1 teknium1 added sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 13, 2026
@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the contribution. Closing as a duplicate: #26471 by @JackALaing submitted the same lineage-fallback fix earlier the same day (May 15) with regression tests, and the version that ultimately landed (PR #66058, salvaging #50575) generalizes the retry into a shared helper with broader coverage. The collision → #N suffix behavior you targeted is now on main.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/sessions Session lifecycle, resume, persistence, history comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants