[BUG] Session title permanently blank after /clear — UserPromptSubmit workaround defeated by unchanged-title no-op (follow-on to #67389)
Summary
After /clear, the session title bar goes blank and never returns — not on the next prompt, and not after a full claude -c resume — even though a UserPromptSubmit hook emits the correct sessionTitle. The title can only be restored by emitting a different value, which proves the apply path is being short-circuited by the unchanged-title no-op while /clear leaves the cached title (and on-disk customTitle) intact.
This is the follow-on to #67389 (SessionStart clear discards sessionTitle). #67389 documents that the UserPromptSubmit path is "the workaround" for /clear — but in 2.1.x that workaround does not actually work, for the reasons below.
Environment
- CLI: 2.1.177 (also observed on 2.1.173)
- OS: macOS (darwin 25.5.0)
- Area: hooks / session title
Minimal repro
- Add a
UserPromptSubmithook that always sets a fixed title:
``jsonc``
// ~/.claude/settings.json
"UserPromptSubmit": [{ "hooks": [{ "type": "command", "command": "echo '{\"hookSpecificOutput\":{\"hookEventName\":\"UserPromptSubmit\",\"sessionTitle\":\"MyTitle\"}}'" }]}]
- Start a session, submit a prompt → title bar shows
MyTitle. ✅ - Run
/clear→ title bar goes blank (expected per #67389; clear-source emit is discarded). - Submit another prompt → the hook emits
MyTitleagain, but the title bar stays blank. ❌ /exit, thenclaude -cto resume → title bar still blank, despitecustomTitle: "MyTitle"being present on the first line of the session.jsonl. ❌- Temporarily change the hook to emit a different value (e.g.
MyTitle X) and submit a prompt → title bar rendersMyTitle X. ✅ → confirms the apply path works and the only blocker in step 4 is the equal-value no-op.
Expected
After /clear, the first UserPromptSubmit that emits a sessionTitle should re-render the title bar (and/or claude -c should rehydrate the title bar from the persisted customTitle).
Actual
The title bar is permanently blank for the life of the conversation once /clear is used, unless the hook emits a value that differs from the previously-applied title.
Root cause (from the bundled build; minified names are build-specific)
Three interacting behaviors:
clear-source discard (#67389):... = source === "startup" || source === "resume" ? title : void 0— the SessionStart emit on/clearis dropped.- Unchanged-title no-op, with a stale cache across
/clear: the apply/cache paths early-return when the sanitized emit equals the current cached title:
``js`
let _ = sanitize(incoming); if (!_) return;
let cur = currentTitle(); // resolves to the cached title / customTitle
if (_ === (cur && sanitize(cur))) return; // no-op
// ... "Hook sessionTitle applied (N chars)" / "... cached (N chars)"
/clear
blanks the **rendered** title but does **not** reset this cached "current" title. So re-emitting the same (correct) title from UserPromptSubmit` matches the cache and no-ops → the bar stays blank.
- Resume doesn't rehydrate the bar from
customTitle:claude -cresumes the session (whose first.jsonlrecord still hascustomTitle: "MyTitle") but does not render the bar from it, andresumeisn't honored by the SessionStart path for hooks either.
Net: there is no value a hook can emit that both (a) differs from the cache (to pass the no-op) and (b) is visually identical to the intended title — so no clean hook workaround exists. A zero-width-suffix trick renders once, then becomes the cache and re-sticks on the next /clear.
Suggested fix (any one breaks the deadlock)
- On
/clear, reset the cached "current" title (and ideally re-render the bar fromcustomTitle), so a subsequent equal-value emit applies; or - Have
claude -crehydrate the title bar from the persistedcustomTitle; or - Honor
sessionTitlefor SessionStart sourceclear(the #67389 ask), which would make the bar correct immediately without relying on the no-op'd re-emit.
Evidence
Verified empirically with a hook that logs its input + decision: post-/clear UserPromptSubmit received session_title: "<derived>" and emitted "<derived>", yet the bar stayed blank; emitting "<derived> __TEST" rendered immediately. customTitle on disk remained "<derived>" throughout. Inspected the 2.1.177 binary for the source-filter and no-op branches quoted above.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗