Dispatch: "Another Claude Code session is already active in this directory" names no session; plus a stale scheduled_tasks.lock whose own PID-reuse guard is never checked

Status Open
Reported on v2.1.258
Maintainer reply None cached
Activity 0 comments · opened Sep 4, 2026

Summary

Two related lock-hygiene problems in the desktop app's session handling. The first cost several hours to diagnose because the error is unactionable; the second is a genuine stale lock that survives because its own staleness check is never applied.

Environment

  • Claude desktop app, bundled CLI 2.1.258
  • claude on PATH is 2.1.138 (version skew, possibly unrelated but noting it)
  • Windows 11

---

1. start_code_task refuses with an error that names nothing

Dispatch repeatedly failed to open a new session in a directory that already had one:

[error] [DispatchTools] start_code_task failed for C:\Users\<user>\...\<repo>:
Another Claude Code session is already active in this directory.

The refusal itself is correct — two sessions editing one working tree is worth preventing. The problem is the message contains none of the information needed to act on it. It does not say which session, when it started, whether it is interactive or background, or what the user can do instead.

That information is sitting right there in the lock the check reads, ~/.claude/sessions/<pid>.json:

{
  "pid": 3236,
  "sessionId": "<uuid>",
  "cwd": "C:\Users\<user>\...\<repo>",
  "procStart": "1343295525...",
  "kind": "interactive",
  "entrypoint": "claude-desktop",
  "name": "assay-90"
}

The record already carries name, kind, entrypoint and a resolvable start time. A message like:

Session assay-90 (interactive, started 20:27) is already active in this directory. End it, or start the new session in a different directory — e.g. a git worktree.

...would have made this self-service. As it stands, the user's only signal is that Dispatch "doesn't work", with no way to tell a live session from a leftover artifact.

Compounding it: a reboot does not change the message, because the session is restored afterwards and re-registers under a new PID with the same sessionId. From the user's side that is indistinguishable from a stale lock that a reboot failed to clear — which is exactly the wrong conclusion to be led toward, and the one we spent the most time chasing.

Suggested fix

Include name / kind / start time from the session record in the error, and ideally suggest the worktree route or offer to route a follow-up to the existing session (the app already does this successfully elsewhere — Routed follow-up to local_<id>).

---

2. .claude/scheduled_tasks.lock goes stale and its PID-reuse guard is not applied

Found in a project directory:

{"sessionId":"<uuid>","pid":9396,"procStart":"134329245736600422","acquiredAt":1788452073730}

The holder was long dead, but PID 9396 had been reused by an unrelated process, so a naive liveness check reports the lock as valid:

Get-Process -Id 9396   ->  ALIVE          (wrong conclusion)

Converting the recorded procStart FILETIME shows the mismatch plainly:

lock's recorded procStart : 2026-09-03 15:56:13 UTC
live PID 9396 actual start: 2026-09-04 00:17:08 UTC   <- 8h21m later

The lock format already stores procStart for exactly this purpose — it is the field that distinguishes "same PID" from "same process". Something writes it and then nothing validates against it, so the lock outlives its holder indefinitely. Deleting the file by hand was the only way to clear it, and nothing recreated it afterward.

Worth contrasting with ~/.claude/sessions/<pid>.json (issue 1), where the same procStart field checks out to the second against the live PID — so the mechanism works there and is simply not applied here.

Suggested fix

Validate procStart against the live process's actual start time before honoring scheduled_tasks.lock, and treat a mismatch as stale. This is the same check the sessions registry appears to rely on.

---

Why these are worth separating

Issue 1 is a UX defect in a check that is otherwise behaving correctly. Issue 2 is a correctness defect in a different lock that silently fails open on PID reuse. They surfaced together, and the first one actively misdirects diagnosis of the second — the natural hypothesis after a reboot doesn't clear things is "stale artifact", which was true of one lock and false of the other.

Happy to split into two issues if preferred.

View original on GitHub ↗