Reliably rename Claude Code sessions from within Claude itself, even with multiple concurrent sessions.
Claude Code stores sessions as JSONL files. To rename a session, you append a custom-title entry. Simple, right?
Wrong. Three problems:
-
Concurrent sessions: With multiple Claude windows open, "most recently modified file" heuristics fail. You might rename the wrong session.
-
Wrong field name: The field is
customTitle, nottitle. Took reverse-engineering to discover. -
Concatenation bug: If you write with a leading newline (
'\n' + entry), Claude's next write concatenates directly after your entry, breaking JSONL parsing:{"type":"custom-title",...}{"parentUuid":...} # BROKEN - two JSON objects on one line
The marker approach definitively identifies THIS session:
- Generate unique marker (UUID-based string)
- Echo it → marker appears in Claude's tool output → gets written to THIS session's JSONL file
- Search all session files for that marker → found file = THIS session
- Append custom-title entry with trailing newline
# Step 1: Generate and echo marker
MARKER=$(python3 rename_session.py marker) && echo "$MARKER"
# Step 2: Find session and rename
python3 rename_session.py rename "Session Name" "$MARKER"The marker gets written to the session file when Step 1 completes. Step 2 searches for it. A single command can't work because the search would run before the marker is written.
# Step 1
MARKER=$(python3 "/path/to/rename_session.py" marker) && echo "$MARKER"
# Step 2 (use marker from step 1)
python3 "/path/to/rename_session.py" rename "My Session Name" "__RENAME_MARKER_abc123__"If you're not in a Claude session, you can still use the fallback:
python3 rename_session.py rename "Name" "no-marker"
# Falls back to time-based heuristics (less reliable){"type": "custom-title", "customTitle": "Session Name", "sessionId": "uuid-here"}Requirements:
- Field is
customTitle(nottitle) - Must include
sessionId - Must end with newline (not start with one)
- Must be valid JSON on its own line
session-renamer/
├── rename_session.py # Main script
├── identify_session.py # Standalone session identification utility
└── README.md # This file
Sessions are stored at:
~/.claude/projects/[project-key]/[uuid].jsonl
Where [project-key] is your working directory with / replaced by -.
Each line is a JSON object:
- User messages
- Assistant responses (including tool calls)
- Tool results
- Metadata (like
custom-title)
Claude Code reads these files to populate /resume and restore conversations.
This solution came from debugging why auto-rename kept failing:
- Initial approach: Most recently modified file → failed with concurrent sessions
- Second attempt: Smallest recent file → still unreliable
- Time window (5 seconds): Better but still prone to race conditions
- Marker approach: 100% reliable - unique marker only exists in one file
We also discovered:
- The field is
customTitlenottitle(found by examining working renamed sessions) - Trailing newline is critical (Claude was concatenating our entries with its next write)
fsync()doesn't help - the issue was format, not flushing
_ Operations/poke/parkit.py- Uses marker approach for/pausecommand~/.claude/commands/wrapup.md- Uses marker approach for session wrapup~/.claude/commands/pause.md- Uses marker approach for pausing sessions
Public gists: