[BUG] --resume lists sessionKind:bg sessions that --continue refuses to resume, with a title identical to their parent
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?
claude --resume and claude --continue disagree about whether a sessionKind: bg session is a resumable conversation. The picker lists it; --continue refuses it. And because such a session's first user message is byte-identical to its parent's, the picker renders the same title twice, distinguishable only by a two-character bg chip.
What the picker shows — two rows, same title, minutes apart:
❯ Investigate the failing integration test and open a bug upstream…
19 seconds ago · bg · HEAD · 91.3KB
Investigate the failing integration test and open a bug upstream…
24 seconds ago · HEAD · 90.8KB
There is no way from that display to tell which row is the conversation you were having and which is the background one — the titles are identical because they are derived from the same first message, and bg is not explained anywhere I could find.
Meanwhile --continue treats the same session as not-resumable: with a bg session as the newest file in a project directory, claude --continue skipped it and attached to the older ordinary session instead. So one resume path offers a session that the other path declines to resume.
This looks like an inconsistency rather than a deliberate difference, because the filtering machinery is otherwise thorough and consistent. #84421 documents that the picker deliberately drops sdk-ts / sdk-py / sdk-cli entrypoints and quotes the debug line Session <id> filtered from /resume: entrypoint=sdk-ts, noting that "hiding programmatic sessions (subagents, /loop, daemons) by default is reasonable." A backgrounded-agent session is a programmatic session by that same standard, --continue already treats it as one, and the picker does not.
Update — the creating action is now measured. When I filed this I could only say that bg sessions existed and that backgrounding was a strong candidate; I had not instrumented the moment of creation. I have since reproduced it end to end.
Exiting with background work in flight offers a three-way prompt:
Background work is running
The following will stop when you exit:
subagent · Sleep 150 then echo
shell · sleep 150; echo done
❯ 1. Exit and stop tasks
2. Move to background and exit
3. Stay
Choosing option 2 creates the second session. In a directory that had exactly one session, immediately after choosing it:
3acd10e4-c502… sessionKind=None 17:00:02 first user: 'Use the Agent tool to dispatch a general-purpose s'
a2a0f69d-0b0b… sessionKind='bg' 17:00:10 first user: 'Use the Agent tool to dispatch a general-purpose s'
Byte-identical first messages, 8 seconds apart. claude --resume in that directory then renders exactly the duplicate this issue is about — and this is real output, not the constructed lab from the steps below:
❯ Dispatch subagent sleep task
3 seconds ago · bg · HEAD · 101.7KB
Dispatch subagent sleep task
32 seconds ago · HEAD · 71.6KB
So the sequence is: background work in flight → "Move to background and exit" → a new top-level sessionKind: bg session seeded with the parent's prompt → two picker rows with the same title, the bg one newer, distinguished only by the chip.
What Should Happen?
Either of these would resolve it, and they point in opposite directions, so this is really a request for the two paths to agree:
- Filter
sessionKind: bgfrom the picker too, matching what--continuealready does and matching the treatment of other programmatic sessions. This is the smaller change and the one consistent with the existing filter set. - Or keep listing it and make it legible — a label a human can read rather than
bg, some disambiguation from the parent it shares a title with, and--continueable to attach to it.
Separately and regardless of which is chosen: the picker should not render two rows with identical titles and no other distinguishing information. Deriving the title from the first user message means a session seeded with its parent's prompt is indistinguishable from that parent.
Error Messages/Logs
# Two sessions in one project dir, byte-identical first user messages.
# Only one carries sessionKind: bg.
$ python3 - <<'PY'
import json, pathlib
p = pathlib.Path.home()/".claude/projects/<slug>"
for f in sorted(p.glob("*.jsonl")):
kind = first = None
for line in f.open():
d = json.loads(line)
if kind is None and "sessionKind" in d: kind = d["sessionKind"]
if first is None and d.get("type") == "user":
c = d["message"]["content"]
first = c[:48] if isinstance(c, str) else c[0].get("text","")[:48]
print(f"{f.name[:13]} kind={kind or '-':4} {first!r}")
PY
6905a9e2-f74… kind=bg 'The task is to use the Agent tool to drive the …'
399441c9-98a… kind=- 'The task is to use the Agent tool to drive the …'
# --continue, with the bg session as the NEWEST file, attaches to the OTHER one:
$ cat ~/.claude/sessions/23034.json
{"pid": 23034, "sessionId": "11111111-…", "kind": "interactive", "entrypoint": "cli"}
Steps to Reproduce
Observed first on real project directories, then reproduced in an isolated one. The isolated version is deterministic:
- Pick a scratch directory to act as the project cwd. Work out its project-dir slug the way Claude Code does — NFC-normalize the absolute path, then replace every non-alphanumeric character with
-— and create~/.claude/projects/<slug>/.
- Write an ordinary session
A.jsonl: take the first ~12user/assistantrecords from any real transcript and rewritesessionIdandcwdto the new session id and scratch path.
- Write a second session
B.jsonlfrom the same records (so the first user message, and therefore the title, is identical), rewritingsessionId, and adding"sessionKind": "bg"to each record.
os.utimethe files so that B is newer than A.
- Run
claude --resumein the scratch directory. Observed: both A and B are listed, with identical titles; B carries a· bg ·chip.
- Run
claude --continuein the scratch directory. Observed: it attaches to A, the older ordinary session, not to the newer B.
To confirm which session --continue actually attached to, read ~/.claude/sessions/<pid>.json — it records pid, sessionId, cwd, kind and entrypoint for the live process. Do not try to read this off the terminal: on a large session a resume-mode prompt renders before any conversation text, so inspecting the visible pane gives a confident wrong answer.
Tear the scratch project directory down afterwards — it is inside the live picker's data and will otherwise keep appearing in your real --resume list.
Claude Model
Not sure / Multiple models
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.226 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Other
Additional Information
Terminal was tmux 3.6a on macOS.
Measured filter behaviour on 2.1.226, for context on where bg sits relative to the other classes:
| Session class | /resume picker | --continue |
|---|---|---|
| isSidechain: true (subagent) | not listed | skipped |
| same records with isSidechain removed | listed (positive control) | — |
| entrypoint: sdk-cli | not listed | not tested |
| sessionKind: bg | listed, as · bg · | skipped |
| entrypoint: cli | listed (control) | attached |
The executable carries matching filter-reason strings: filtered from /resume: isSidechain=true, teamName=, sessionKind=, entrypoint=, and /loop session. The presence of a sessionKind= filter alongside the observation that bg is not filtered is what suggests the two paths have simply drifted apart rather than differing by design.
One incidental note in case it is useful: subagent transcripts live nested at <slug>/<session-uuid>/subagents/agent-<id>.jsonl, and across 1064 top-level session files on this machine, none contained an isSidechain record — so the isSidechain filter appears to be defence-in-depth against a case that does not arise in normal use, while bg is the class that actually reaches the picker.
Related: #84421 (the inverse request — sdk-* sessions are filtered and some users want them shown; establishes that the filtering is deliberate and quotes the debug log line). #84281 is a separate picker rendering issue.
---
Environment summary (added after filing, for scanning): Claude Code 2.1.226 CLI (TUI resume picker) on macOS, tmux 3.6a, Anthropic API. Reproduction steps are in Steps to Reproduce above and are deterministic — they build an isolated project directory from real session records rather than depending on my machine's state.
Filed alongside #85654 from the same investigation. That one is a separate defect with a separate cause (input dispatch), and the two do not overlap in fix; noting it here only because a reader landing on one will usually want the other.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗