Bug: countConcurrentSessions() deletes non-.json files from ~/.claude/sessions/

Resolved 💬 3 comments Opened Mar 25, 2026 by flute Closed Mar 28, 2026

Description

The countConcurrentSessions() function in the CLI scans ~/.claude/sessions/ and deletes files belonging to "dead processes". However, it incorrectly treats non-.json files (e.g., .tmp files from plugins) as PID files, parses the year from date-prefixed filenames as a PID, and deletes them.

Root Cause

From the bundled cli.js (deminified):

async function countConcurrentSessions() {
  const sessionsDir = path.join(getClaudeDir(), "sessions");
  const files = await readdir(sessionsDir);

  for (const file of files) {
    const pid = parseInt(file.replace(/\.json$/, ""), 10);
    if (isNaN(pid)) continue;

    if (pid === process.pid) { count++; continue; }

    if (isProcessRunning(pid)) {
      count++;
    } else {
      // Deletes the file if the "PID" process isn't running
      unlink(path.join(sessionsDir, file)).catch(() => {});
    }
  }
}

The problem: parseInt("2026-03-24-ecosystem-int-session.tmp", 10) returns 2026 (stops at the first -). Since PID 2026 is not running, the file is deleted.

Reproduction

  1. Install a plugin that writes .tmp files to ~/.claude/sessions/ (e.g., everything-claude-code which uses this directory for session persistence via /save-session)
  2. Run /save-session — creates ~/.claude/sessions/2026-03-24-<id>-session.tmp (259 lines of session state)
  3. Exit and start a new Claude Code session
  4. The .tmp file is gone — deleted by countConcurrentSessions() because parseInt("2026-...") = 2026 and PID 2026 is not running

Impact

  • Any plugin storing non-.json files in ~/.claude/sessions/ will have its files silently deleted
  • The everything-claude-code plugin (5k+ GitHub stars) uses this directory for session save/resume functionality, which is completely broken by this bug
  • Users lose session state with no error message or warning

Suggested Fix

Only process .json files in the cleanup loop:

for (const file of files) {
  if (!file.endsWith('.json')) continue;  // Skip non-JSON files
  const pid = parseInt(file.replace(/\.json$/, ""), 10);
  if (isNaN(pid)) continue;
  // ... rest of logic
}

Or alternatively, validate that the entire filename (minus .json) is a valid integer:

const basename = file.replace(/\.json$/, "");
const pid = Number(basename);
if (!Number.isInteger(pid) || String(pid) !== basename) continue;

Environment

  • Claude Code version: 2.1.81
  • OS: macOS Darwin 25.2.0
  • Node: v22.22.0

Workaround

The ECC plugin has submitted a PR to move its session storage to ~/.claude/ecc-sessions/ to avoid this directory entirely: https://github.com/affaan-m/everything-claude-code/pull/899

View original on GitHub ↗

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