Skip to content

fix(server): one greedy agent process no longer takes down the whole server - #5788

Merged
t3dotgg merged 1 commit into
mainfrom
t3code/oom-policy-continue
Aug 9, 2026
Merged

fix(server): one greedy agent process no longer takes down the whole server#5788
t3dotgg merged 1 commit into
mainfrom
t3code/oom-policy-continue

Conversation

@t3dotgg

@t3dotgg t3dotgg commented Aug 9, 2026

Copy link
Copy Markdown
Member

Problem

My connection kept dropping with Relay environment endpoint is unavailable: endpoint_request_failed, and it looked like a relay bug. It was not. The server was being killed and restarted underneath me.

An agent was running memory-hungry python scripts in a worktree. Three times in one hour they grew past 50 GB and the kernel OOM killer reaped them. That part is fine and working as intended.

The problem is what happened next. Agent tool calls run as children of the server, so they live in the same t3code.service cgroup. systemd defaults to OOMPolicy=stop, which means killing any one process in a unit stops the entire unit. So one bad python script took down the server, every live agent, and my ssh sessions:

t3code.service: The kernel OOM killer killed some processes in this unit.
t3code.service: Killing process 2065546 (claude) with signal SIGKILL.
t3code.service: Failed with result 'oom-kill'.

cloudflared survives those restarts, so the tunnel stays up and points at a dead origin. The relay's mint-credential request then gets:

Unable to reach the origin service ... dial tcp 127.0.0.1:3773: connect: connection refused
dest=.../api/t3-connect/mint-credential

which maps to EnvironmentMintRequestFailed and surfaces as the banner.

Solution

Set OOMPolicy=continue on the generated unit. The kernel still reaps the greedy child, but the server keeps running and nobody gets disconnected. Restart=always still covers the case where the main process itself dies.

Existing installs heal themselves. status.current compares the installed unit against the rendered one, so the next update rewrites it.

Verification

  • vp test run apps/server/src/cloud/bootService.test.ts passes (7 tests).
  • systemd-analyze verify accepts the rendered unit on systemd 259.
  • Typecheck on apps/server is clean.

Written by Claude Opus 5 (1M context) in Claude Code, driven by Theo.


Note

Medium Risk
Changes long-running service supervision behavior on Linux; misconfiguration could leave a degraded server running after OOM, though scope is limited to the generated unit and existing restart policy.

Overview
Prevents a single OOM-killed agent child from stopping the whole t3code.service unit.

renderBootServiceUnit now emits OOMPolicy=continue in the generated systemd user unit. Agent tool processes share the server’s cgroup; with systemd’s default OOMPolicy=stop, the kernel reaping one memory-heavy child could tear down the server, other agents, and live sessions. The greedy process still dies; the main server keeps running, with Restart=always unchanged for when the main process itself exits.

A unit test asserts the rendered unit includes OOMPolicy=continue. Installs that compare the on-disk unit to the rendered template pick up the change on the next repair/update.

Reviewed by Cursor Bugbot for commit 3cf0584. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Prevent OOM-killed agent child processes from stopping the server

Adds OOMPolicy=continue to the systemd unit generated by renderBootServiceUnit in bootService.ts. This tells systemd to keep the parent service running when a child process is OOM-killed, rather than stopping the whole unit. A test is added to assert the directive is present in the generated unit.

Macroscope summarized 3cf0584.

Agent tool calls run as children of the server, so they share the
t3code.service cgroup. systemd defaults to OOMPolicy=stop, so when the
kernel OOM-killed one greedy child it stopped the whole unit: server,
every live agent, and the user's relay connection.

Set OOMPolicy=continue. The kernel still reaps the greedy child, the
server keeps running, and Restart=always still covers the main process.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a9704e95-810a-4c6f-8b3d-d60fdd3d24ae

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XS 0-9 changed lines (additions + deletions). labels Aug 9, 2026
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 11.3 KiB 11.3 KiB +5 B (+0.0%) 15.1 KiB
Codex Thread snapshot wire 5.5 KiB 5.5 KiB −8 B (−0.1%) 7.3 KiB
Codex Live turn WebSocket wire 5.9 KiB 5.9 KiB +13 B (+0.2%) 7.8 KiB
Codex Live turn WebSocket decoded 49.7 KiB 49.7 KiB 0 B (0.0%) 66.4 KiB
Codex Live turn messages 16 16 0 (0.0%) 21
Claude Total thread wire 11.3 KiB 11.3 KiB −8 B (−0.1%) 15.1 KiB
Claude Thread snapshot wire 5.5 KiB 5.5 KiB −10 B (−0.2%) 7.3 KiB
Claude Live turn WebSocket wire 5.9 KiB 5.9 KiB +2 B (+0.0%) 7.8 KiB
Claude Live turn WebSocket decoded 50.6 KiB 50.6 KiB 0 B (0.0%) 66.4 KiB
Claude Live turn messages 16 16 0 (0.0%) 21

Baseline: 05eb051 · PR result: 3cf0584 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 94.6 KiB
  • Claude decoded thread snapshot: 95.4 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@macroscopeapp

macroscopeapp Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved 3cf0584

Adds a single systemd directive OOMPolicy=continue to prevent OOM-killed child processes from stopping the entire service. This is a minimal, well-documented resilience fix with test coverage, made by the file's primary author.

You can customize Macroscope's approvability policy. Learn more.

@t3dotgg
t3dotgg merged commit ba9c9ae into main Aug 9, 2026
17 checks passed
@t3dotgg
t3dotgg deleted the t3code/oom-policy-continue branch August 9, 2026 05:13
github-actions Bot added a commit to omarcresp/t3code-flake that referenced this pull request Aug 9, 2026
## What's Changed
* feat(web): project icons can be chosen manually by @t3dotgg in pingdotgg/t3code#5775
* fix(server): one greedy agent process no longer takes down the whole server by @t3dotgg in pingdotgg/t3code#5788


**Full Changelog**: pingdotgg/t3code@v0.0.33-nightly.20260809.1040...v0.0.33-nightly.20260809.1041

Upstream release: https://github.com/pingdotgg/t3code/releases/tag/v0.0.33-nightly.20260809.1041
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XS 0-9 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant