[BUG] Claude Code escapes systemd transient scope at startup, defeating user-imposed memory caps

Resolved 💬 3 comments Opened May 19, 2026 by CRCinAU Closed Jun 18, 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?

Wrapping claude in systemd-run --user --scope -p MemoryMax=… does not bound Claude's memory usage. The process actively migrates itself out of the transient scope into the parent terminal's session scope within ~2 seconds of starting, leaving the scope empty and getting GC'd. The cap is then irrelevant. On a host where Claude's working set grows unbounded (e.g. context-heavy session, large MCP servers), it can OOM the entire machine instead of being OOM-killed inside its own cgroup. This is a real reliability/safety issue for users running Claude on workstations.

What Should Happen?

Claude should not write its PID to a different cgroup at startup. If a wrapper places Claude in a scope/slice with limits, those limits should hold for the entire process lifetime. At minimum, the migration behavior should be:

  • documented,
  • gated behind an opt-in flag, and
  • disabled when the binary was launched via systemd-run --scope (detectable via $INVOCATION_ID or the initial cgroup path containing /run-p).

Error Messages/Logs

Environment

  - Claude Code 2.1.144 (/home/.../.local/share/claude/versions/2.1.144, single ELF binary)
  - Fedora, Linux 7.1.0-rc3, cgroups v2, user@1000.service active
  - Launched from KDE Konsole via bash -l, with a wrapper function:


  claude() {
    local _bin="/home/me/.local/bin/claude"
    if [ -S "${XDG_RUNTIME_DIR}/systemd/private" ]; then
      systemd-run --user --scope --quiet \
        -p MemoryMax=20G -p MemorySwapMax=0 -p TasksMax=500 \
        -- "$_bin" "$@"
    else
      "$_bin" "$@"
    fi
  }

Steps to Reproduce

  1. Start Claude through a systemd-run --user --scope -p MemoryMax=… wrapper.
  2. Watch journalctl --user -f and the process cgroup:

pid=$(pgrep -f /home/me/.local/bin/claude)
awk -F: '{print $3}' /proc/$pid/cgroup

  1. Observe:
  • Scope is created: Started run-pPID-iXXXX.scope - [systemd-run] … claude
  • ~2 s later: run-pPID-iXXXX.scope: Consumed Ns CPU time over Ns wall clock time, NNNM memory peak.
  • Claude PID is unchanged and still running, but now reports cgroup …/app-org.kde.konsole-.scope/tab().scope.
  • The transient scope has been removed.

Claude Model

None

Is this a regression?

Yes, this worked in a previous version

Last Working Version

_No response_

Claude Code Version

2.1.144

Platform

Anthropic API

Operating System

Other Linux

Terminal/Shell

Other

Additional Information

A user trying to defensively limit Claude's blast radius on a shared workstation cannot. The single user-space mitigation that should "just work" on a modern Linux desktop — wrapping the command in a transient systemd scope — is silently defeated by the process itself. The only remaining options are slice-level limits on user.slice (over-broad) or RLIMIT_AS (imprecise for V8). A runaway Claude takes down the whole host.

View original on GitHub ↗

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