#7851·omnigent

[Bug] Renaming a session to a title containing `:closed:` makes it reject every message

Author: lavneethoraCreated Sep 19, 2026Updated Sep 20, 2026
LabelsBugcomp:servercomp:reprP2-mediumtriagedvalidated:reproduced

Description

Renaming a session to a title containing :closed: makes that session reject every later message with 409 Session is closed, truncates the title, and marks the session closed in the API. Nothing is reported at rename time.

sys_session_close historically freed the (parent_conversation_id, title) unique slot by appending ":closed:<the closed child's id>" to a child's title. has_closed_title_marker in omnigent/util/session_lifecycle.py still detects that legacy state by testing for the bare substring:

python
return bool(title and CLOSED_TITLE_INFIX in title)

PATCH /v1/sessions/{id} writes the caller's title straight to the store, so any title carrying those characters is read back as internal state. Everything downstream follows: title_without_closed_marker truncates the title at the marker for display, labels_with_closed_status synthesizes omnigent.closed=true into API responses, and the events route refuses new user messages.

Observed on main @ f8e1e4bf through the real routes:

Title set Title returned Labels Next message
release:closed:beta release {"omnigent.closed": "true"} 409 Session is closed
notes about a :closed: door notes about a {"omnigent.closed": "true"} 409 Session is closed

It is recoverable: renaming to a title without those characters restores the session, since the label is derived rather than persisted. Typing :closed: is uncommon, though titles are not always hand-written, as the background title generator also writes them.

Proposed fix: the marker always ends with the row's own id, so match that exact suffix rather than the substring, threading the conversation id into the four helpers and their call sites. A user title can then never be mistaken for internal state, and every legacy row is still recognised. Validating titles at rename time would be an alternative, but it would not help rows whose titles were set by other paths.

Steps to reproduce

  1. Create a session and note its id.
  2. Rename it:
bash
curl -X PATCH "$OMNIGENT_URL/v1/sessions/$SESSION_ID" \
  -H 'content-type: application/json' \
  -d '{"title": "release:closed:beta"}'

The response shows "title": "release" and "labels": {"omnigent.closed": "true"}.

  1. Send a message to that session:
bash
curl -X POST "$OMNIGENT_URL/v1/sessions/$SESSION_ID/events" \
  -H 'content-type: application/json' \
  -d '{"type": "message", "data": {"role": "user", "content": [{"type": "input_text", "text": "hi"}]}}'

The response is 409 Session is closed. Start a new sub-agent session to continue.

Expected: a title is user text. Renaming a session leaves it open and displays the title as written.

Version

main @ f876b854

OS

macOS

Harness

No response

Harness mode

No response

Platform or device

macOS

Observed impact

None

Authentication type

No response