Skip to content

macOS desktop main process hangs under swap pressure from uncapped agent children on a 16 GB Mac (0.1.0) #24048

Open

Description

@gaspardhassenforder

What happened

T3 Code became unresponsive together with the other RAM-heavy applications on my Mac. In Activity Monitor the process showed 1.87 TB of virtual memory; how can that be?

The desktop app had been running for about 3 days with two Claude threads and three Codex delegated review tasks active. The Mac began swapping, the T3 Code main process stopped responding, and it had to be relaunched. The 1.87 TB virtual size turned out to be ordinary V8 address-space reservation (a freshly started server shows the same figure); the real signal was 2.3 MB resident, 32 KB private and "recent hangs: 36" on the main process.

Diagnosis

On macOS, agent commands run with no memory limit. apps/server/src/process/agentScope.ts (v0.1.0 line 112, and still on main) returns AgentScope.defaultValue() for every platform other than Linux, where agents get a systemd slice with MemoryHigh/MemoryMax. On a 16 GB Mac, two Claude threads plus Codex delegates whose children ran test suites and headless Chromium were enough to push the machine into heavy swapping. T3's own processes were then paged out: the embedded server logged rising major page faults and shrinking RSS for nine minutes, then the desktop main process went silent for 266 s with no quit record, no crash report and no OOM kill. Nothing in the app's logs records the hang itself (same gap as #21301).

The T3 process tree held about 2.5 GB of 8 GB resident after the restart, so T3 was both a contributor (uncapped agent children) and a victim of the swapping.

Steps to reproduce

Not deterministic. Observed once; no controlled repro was run.

  1. On a 16 GB Mac with other memory-heavy apps open, run the desktop app for a few days without restarting.
  2. Run two Claude threads in parallel and let one delegate review tasks to Codex; let the agents run test suites and headless browsers.
  3. When the Mac starts swapping, the T3 Code main process stops responding and leaves no record.

Version

0.1.0 (desktop app and embedded server; the t3 triage CLI was 0.1.2)

Environment

macOS 27.0.0, Apple Silicon, 16 GB RAM, desktop app with local server; Node v25.4.0 on PATH; claude 2.1.309; codex-cli 0.163.0

Evidence

# event-loop-stalls.ndjson (server), 2026-10-23 UTC. majorPageFaults rises, rssMb falls: the server is being swapped out.
{"time":"2026-10-23T08:38:11.899Z","delayMaxMs":5157,"utilization":0.31,"majorPageFaults":94,"minorPageFaults":70204,"rssMb":115}
{"time":"2026-10-23T08:38:41.905Z","delayMaxMs":3467,"utilization":0.19,"majorPageFaults":178,"minorPageFaults":49712,"rssMb":113}
{"time":"2026-10-23T08:45:12.020Z","delayMaxMs":2504,"utilization":0.28,"majorPageFaults":484,"minorPageFaults":64981,"rssMb":105}
{"time":"2026-10-23T08:46:23.045Z","delayMaxMs":2584,"utilization":0.35,"majorPageFaults":1610,"minorPageFaults":54595,"rssMb":59}
{"time":"2026-10-23T08:46:54.665Z","delayMaxMs":4927,"utilization":0.92,"majorPageFaults":3629,"minorPageFaults":95702,"rssMb":38}
{"time":"2026-10-23T08:47:27.315Z","delayMaxMs":5837,"utilization":0.82,"majorPageFaults":3937,"minorPageFaults":42547,"rssMb":184}

# desktop.trace.ndjson: main-process spans per minute, then a 266 s gap with no quit/shutdown span
08:44 290   08:45 183   08:46 171   [gap 08:46:49 -> 08:51:15, 266 s]   08:51 750 (desktop.startup after relaunch)

# renderer-history.ndjson: main renderer working set, same session (started 2026-10-20)
08:45:16  workingSetBytes 262848512   peakWorkingSetBytes 1258799104
08:46:56  workingSetBytes 9043968     peakWorkingSetBytes 1258799104   <- paged out

# Activity Monitor on the hung main process at 10:50:55 local
real 2.3 MB, virtual 1.87 TB, shared 61.3 MB, private 32 KB, CPU 0.00 %, recent hangs 36, parent launchd

# machine at triage time
vm.swapusage: total = 5120.00M  used = 3943.00M  free = 1177.00M
memory_pressure: Pageouts 151588 ; kern.memorystatus_vm_pressure_level: 1 (recovered after relaunch)
RSS under the T3 process tree after restart: 2483 MB of 8002 MB total

# source, v0.1.0 apps/server/src/process/agentScope.ts
111:  const platform = yield* HostProcess.Platform;
112:  if (platform !== "linux") return AgentScope.defaultValue();

Related issues

#20329 is the same root cause (agent memory limits are Linux-only) but is closed as completed and locked, and the fix it refers to (#20330) was closed unmerged; this report adds a stable-release, 16 GB, low-concurrency data point. #21301 covers the missing hang record. #18485 is a Windows report attributed to MCP helpers under Codex.

Fix applied or workaround

Nothing changed on the machine. Workaround: relaunch the desktop app every day or two (the renderer had grown to 1.26 GB peak over 3 days), run fewer test-heavy threads at once, close other memory-heavy apps while agents run tests.

Filed by

Claude Fable 5.1 (claude-fable-5-1) in Claude Code, via t3 triage, on behalf of the user.

Activity

  1. calebfender commented on Oct 26, 2026

    @calebfender

    Another occurrence on 0.1.2 (stable desktop app, macOS 27.0 arm64, 16 GB RAM). This time it ended in a system-wide out-of-memory prompt, not just swapping.

    What happened

    One Claude thread (Opus 5.5) was doing disk forensics. It started an 8 GB Colima (VZ) VM, read blocks from a device-mapper snapshot of a full disk image inside it, and streamed a whole remote logical volume through ssh … dd bs=16M | sha256sum in backgrounded subshells. At about 22:10Z macOS showed "Your system has run out of application memory". Force Quit listed T3 Code at 190.97 GB, so macOS was charging every agent child (the VM included) to the app. The user force-quit T3.

    T3's own processes were small just before this: the renderer working set was about 290 MB (renderer-history.ndjson), and the server's last event-loop-stalls.ndjson sample shows rssMb: 117, a 2.1 s delay and 685 major page faults. So nearly all of the footprint was uncapped agent children, which matches the diagnosis above. apps/server/src/process/agentScope.ts:96 in v0.1.2 still returns AgentScope.defaultValue() for every platform except Linux.

    Knock-on effect after the force quit

    When the app was reopened at 22:11:00Z and again at 22:11:13Z, the backend exited with code 78 and ServerAlreadyRunningError: A T3 Code server already owns <T3 home>, and the "This T3 home is unavailable" dialog appeared. The previous server's last trace entry is at 22:10:46Z, when it was still shutting down (closing OpenCode servers, stopping the relay client). By the time of triage it had exited, and nothing held server-owner.sqlite. This looks like the leftover-server case from #22983, reached through a force quit under memory pressure instead of Cmd-Q.

    Suggestion

    Even without a cgroup equivalent on macOS, a per-thread watchdog on the agent process tree's footprint (warn, then stop the turn) would have stopped this before the OS did.


    Filed via t3 triage by Claude Code (Opus 5.5, claude-opus-5-5).

  2. calebfender commented on Oct 26, 2026

    @calebfender
    Image Image
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions