[BUG] A renamed session reverts to its derived <dir>-<nn> name after any restart, so ListAgents and SendMessage can no longer reach it
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Short version: a session renamed with /rename loses that name on every restart and comes back as <dir>-<nn>, so peers can't reach it by name any more. The name is still on disk, resume just never reads it back. There's a working workaround below — a small process wrapper, tested on two machines — for anyone who needs this fixed today rather than upstream.
If I rename a session, the name only lasts as long as the process. Reboot, or hit Developer: Reload Window in VS Code, and the session comes back as <dir>-<nn> again. That derived name is the one ListAgents shows and the one SendMessage wants, so after a restart nobody can reach the session by the name I gave it.
The annoying part is that the name is still sitting on disk. Two copies of it:
~/.claude/projects/<slug>/<session-id>.jsonl, as{"type":"agent-name","agentName":"..."}(these show up from 2.1.270 on)~/.claude/projects/<slug>/<session-id>/custom-title.json, as{"customTitle":"..."}
Neither gets read on resume. The name ListAgents and SendMessage go by lives only in ~/.claude/sessions/<pid>.json, that file is keyed by PID, and it's deleted when the process exits. Resume makes a fresh one with nameSource: "derived" and nameSince == startedAt, so it's just minted at spawn without looking anything up.
Not the same as #86736. That one is about /rename not reaching the registry, which works on 2.1.278. This is only about the read-back when a session resumes.
What Should Happen?
If a session has a stored name, resume should use it and set nameSource: "user", so peers can keep addressing it. Fall back to <dir>-<nn> only when there's no stored name.
The pieces are all there already. --name at startup sets the registry name correctly, and the name is on disk in two formats. It's just the lookup that's missing. The workaround below fixes it completely and all it does is pass --name.
Error Messages/Logs
# Same session id, same version, either side of one Developer: Reload Window.
# before
pid=41207 ver=2.1.278 sid=a1b2c3d4 name=my-session nameSource=user
nameSince=1758000757116 startedAt=1758000644976 <- differ, rename applied
# after, same session resumed
pid=41882 ver=2.1.278 sid=a1b2c3d4 name=myproject-7a nameSource=derived
nameSince == startedAt <- minted at spawn
# name is still on disk, untouched
~/.claude/projects/-home-user-myproject/a1b2c3d4-....jsonl
line 471 {"type":"custom-title","customTitle":"my-session",...}
line 473 {"type":"agent-name","agentName":"my-session",...}
~/.claude/projects/-home-user-myproject/a1b2c3d4-.../custom-title.json
{"customTitle":"my-session"}
# old registry entry is gone
$ ls ~/.claude/sessions/41207.json
ls: cannot access '.../41207.json': No such file or directory
Steps to Reproduce
- Start a session and let it run (I'm using the VS Code extension).
/rename my-session. Keep it lowercase and hyphenated, see the note at the bottom for why.- Check it took, either with
ListAgentsfrom another session or straight from the file:
````
$ cat ~/.claude/sessions/<pid>.json
{"pid":...,"sessionId":"...","name":"my-session","nameSource":"user",...}
- Restart the process. Developer: Reload Window, or a reboot. Same session id comes back.
ListAgentsagain, or read the new~/.claude/sessions/<pid>.json.
You get "name":"<dir>-<nn>","nameSource":"derived". The name from step 2 is gone from the registry but still in the transcript's agent-name record and in custom-title.json.
Expected "name":"my-session","nameSource":"user".
Workaround, until this is fixed upstream
Here's what I'm running in the meantime, in case it's useful to anyone else hitting this. It's a process wrapper that looks up the stored name and passes it as --name. The extension calls a wrapper as <wrapper> <real-claude> <args...>, so the real binary comes in as argv[1] and doesn't need hardcoding.
Save it somewhere, chmod +x, and point VS Code at it (machine scope):
{ "claudeCode.claudeProcessWrapper": "/home/you/.local/bin/claude-name-wrapper" }
The script is here, so it can be fixed in place if it needs it:
https://gist.github.com/tamood/6c2e2c25a087777ccacbc86ce3a43346
mkdir -p ~/.local/bin
curl -fsSL https://gist.githubusercontent.com/tamood/6c2e2c25a087777ccacbc86ce3a43346/raw/claude-name-wrapper \
-o ~/.local/bin/claude-name-wrapper
chmod +x ~/.local/bin/claude-name-wrapper
# check before wiring it in: should echo the args back with --name appended
~/.local/bin/claude-name-wrapper /bin/echo --resume=<a-real-session-uuid> --verbose
It's ~110 lines of stdlib Python and does one thing: on a --resume launch, look up the
stored name and append --name <slug>. Every failure path still execs the real binary, so
a bug in it costs you the name, not the session.
Tested on two machines. Between them, 12 sessions restored with nameSource: "user" across four project directories, never-named sessions correctly left on derived names, no misses. On the second machine it was also confirmed from /proc/<pid>/cmdline that --name is appended and that the PID is preserved through execv.
Two things to know before using it. Both follow from the claudeCode.claudeProcessWrapper setting rather than from the script:
- It suppresses the extension's update check —
extension.jsreturns early with"Extension update check: … skipped, a process wrapper is configured", unconditionally. I haven't established whether that is the only update path or a probe alongside the editor's own marketplace updater, so treat it as "you may stop getting extension updates" and check your version manually now and then. - It flips
resolvePermissionModeInClito false, moving permission-mode resolution out of the CLI. This looked like it caused a shift from--permission-mode autotodefault, but an A/B with the wrapper unwired produceddefaulteither way, so the caller appears to always supply the mode explicitly. Real coupling in the code, no observable effect on either machine.
Related note: /rename doesn't always write an agent-name record
/rename only seems to write an agent-name record when the name is already slug-shaped (lowercase, alphanumerics, hyphens). Give it a capital or a space and you get a custom-title record and no agent-name. Both on 2.1.270: Board notes produced custom-title only, while main-worker and test-worker produced both.
This does not make the name unaddressable — the resolver normalizes case and separators at resolution time, so SendMessage to Test Mixed reaches a session listed as test-mixed, and the two are one address written two ways. It only matters for the workaround above, which slugs the stored name, so a session renamed Board notes comes back displayed as board-notes after a restart. Addressing is unaffected; the label changes.
Claude Model
Opus
Is this a regression?
No, this never worked
Claude Code Version
2.1.278 (Claude Code)
Platform
Anthropic API
Environment
- VS Code extension
anthropic.claude-code-2.1.278-linux-x64 - Linux (WSL2)
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗