Skip to content

Child processes (gh, claude -p) are left running after the app exits #12507

Closed

Description

@nojaf

Version: 0.0.42 (t3code-bin on Arch / Omarchy), Linux, Wayland.

After closing T3 Code, subprocesses it spawned keep running, reparented to systemd --user.

I found ten of them on my machine: gh pr list --head ... --json ..., gh pr view <n> --repo ... --json ..., and claude -p --output-format json --json-schema {...} --model claude-haiku-4-5 ... (the title generation call). Some were two days old. They were still inside the app's app-com.t3tools.T3Code-<pid>.scope cgroup while the main T3 Code process was long gone, and the cgroup only got released once I killed them by hand:

app-com.t3tools.T3Code-3219097.scope: Consumed 4d 21h 57min CPU time over 1d 4h 26min wall clock time
app-com.t3tools.T3Code-2913047.scope: Consumed 21h 38min CPU time over 7h 39min wall clock time

No crash was involved: coredumpctl shows no T3 Code crash in that window, so this was a normal quit.

In my case each child got stuck in a loop caused by a wrapper script in my PATH (not T3 Code's fault), which is why they each burned about 12% of a core for days. But the leak itself happens regardless of that: anything gh or claude blocks on (network, auth prompt, a hung helper) will outlive the app.

Expected: the app kills, or at least signals, its child processes on quit, and/or spawns these helper calls with a timeout.

Activity

  1. juliusmarminge commented on Sep 18, 2026

    @juliusmarminge
    Member

    Thanks for the write-up — the leftover gh / claude -p processes after a normal quit look like a real bug.

    What we found

    Those commands match in-tree helper spawns:

    • gh pr list --head … / gh pr view … from apps/server/src/sourceControl/GitHubCli.ts via VcsProcess / apps/server/src/processRunner.ts
    • claude -p --output-format json --json-schema … from apps/server/src/textGeneration/ClaudeTextGeneration.ts (thread title generation)

    On Linux, Effect’s Node spawner defaults to detached: true, so each helper is its own process group. Desktop quit SIGTERMs the backend and escalates to SIGKILL after ~2s (DesktopBackendManager). That does not signal those detached groups. If the backend is hard-killed before its child-scope finalizers run, gh / claude stay in app-com.t3tools.T3Code-<pid>.scope and keep the cgroup alive — which matches what you saw.

    There are timeouts (gh 30s, Claude 180s), but neither path sets forceKillAfter. Scope cleanup sends SIGTERM, waits one second, and never SIGKILLs. A wrapper that ignores SIGTERM (or anything else that stays up) can leak while the app is still running, then outlive quit. Your wrapper explains the 12% CPU spin; the leak itself does not depend on it.

    Not a duplicate of

    No open PR covers Linux app-quit process-tree cleanup for these helpers. #6318 was Windows-only and did not land.

    Next step

    Treat this as process-tree hygiene on quit (and on timeout):

    1. Spawn short-lived helpers with detached: false so they die with the backend process group.
    2. Set forceKillAfter on ProcessRunner and Claude text-generation so a stuck child is SIGKILL’d.
    3. Add a regression that starts a stubborn helper, times out / quits, and asserts it is gone.

    Agree this is not your wrapper’s fault. Leaving this open as an accepted bug.

  2. added
    acceptedfeature request accepted
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Sep 18, 2026
  3. juliusmarminge commented on Oct 20, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    Fixed by #12513, which just merged. On a normal quit or a request timeout, helper processes like gh and claude -p are now force-killed one second after SIGTERM, including their descendants. A hard crash or SIGKILL of the backend is still out of scope.

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

    acceptedfeature request acceptedbugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions