[BUG] A renamed session reverts to its derived <dir>-<nn> name after any restart, so ListAgents and SendMessage can no longer reach it

Status Open
Reported on v2.1.270
Maintainer reply None cached
Activity 2 comments · opened Sep 21, 2026

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

  1. Start a session and let it run (I'm using the VS Code extension).
  2. /rename my-session. Keep it lowercase and hyphenated, see the note at the bottom for why.
  3. Check it took, either with ListAgents from another session or straight from the file:

``
$ cat ~/.claude/sessions/<pid>.json
{"pid":...,"sessionId":"...","name":"my-session","nameSource":"user",...}
``

  1. Restart the process. Developer: Reload Window, or a reboot. Same session id comes back.
  2. ListAgents again, 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.js returns 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 resolvePermissionModeInCli to false, moving permission-mode resolution out of the CLI. This looked like it caused a shift from --permission-mode auto to default, but an A/B with the wrapper unwired produced default either 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)

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗