Repository navigation
Description
Description
host-service.js (the Node child spawned by the Electron main) crashes with V8 FATAL ERROR: Ineffective mark-compacts near heap limit, Allocation failed - JavaScript heap out of memory after a short window of active terminal use. Immediately before the abort, the process is blocked at anon_pipe_write on an IPC pipe to its Electron parent while RSS climbs from ~500 MB to ~5 GB.
The renderer surfaces this as repeated TRPCClientError: Failed to fetch (127.0.0.1:<port>) and "frozen terminals" — the Electron UI itself stays responsive, but terminal panes never get data back.
There are actually two distinct triggers that produce the same crash signature:
- Cold-start trigger — large accumulated state on disk causes host-service to OOM within ~45 s of launch, before any user interaction.
- In-use trigger — even after the cold-start state is cleaned up, host-service still OOMs after ~2–3 min of normal terminal use.
Trigger (1) is recoverable by manual SQL surgery (described below). Trigger (2) appears to be the real underlying bug, and is what the rest of the report focuses on.
Reproduces on every Superset version I tried: 1.9.6, 1.9.7, 1.9.9, 1.11.0, 1.11.1. Not specific to the 1.11 host-service-coupling refactor (PR #4739) and not specific to Claude Code 2.1.149 (repros with 2.1.140 too).
Steps to reproduce
Trigger 1 (cold-start OOM) — reproduces on any install with a few weeks of accumulated Electric/TanStack sync history and stale terminal sessions:
- Use Superset normally over several weeks (multiple workspaces, many terminal sessions).
- Open the app on the v2 dashboard.
- Within ~45 s of launch,
host-service.jsaborts; terminals never become usable. Deterministic across relaunches and across Superset versions (state, not version, drives it).
Trigger 2 (in-use OOM) — reproduces with a freshly cleaned state:
- Apply the workaround in the "Workaround" section below to clear cold-start triggers.
- Launch Superset, open a v2 workspace.
- Attach to or start a terminal pane (
useV2PresetExecution). - Use the terminal for 2–3 minutes (any active TUI works; I used Claude Code).
host-service.jsaccumulates outbound IPC buffer, RSS grows ~500 MB → ~5 GB, then V8 OOM-aborts. Terminals freeze, "Failed to fetch" floods renderer console.
Error output
host-service log around the abort (Trigger 1):
[terminal] respawning lost session 2e0d66b7-68ab-4199-ab26-f25e6b196e41
[terminal] respawning lost session dd6bc1d0-79ec-4933-b996-73a3cdf9b0e7
... (15+ more "respawning lost session" lines)
<--- Last few GCs --->
[498781:0x6e8001c0000] 49319 ms: Scavenge (during sweeping) 3965.2 (3975.1) -> 3965.2 (3982.1) MB
[498781:0x6e8001c0000] 52108 ms: Incremental Mark-Compact (reduce) 3967.6 (3982.1) -> 3967.6 (3970.3) MB
FATAL ERROR: Ineffective mark-compacts near heap limit
Allocation failed - JavaScript heap out of memory
----- Native stack trace -----
host-service process state while wedged (Trigger 2), captured from /proc/<pid>:
State: S (sleeping)
VmRSS: 5128744 kB
wchan: anon_pipe_write
(strace requires elevated ptrace_scope on this kernel; couldn't capture the exact write(fd=…, …) payload from inside the user session. Can capture with sudo if useful.)
Coredumps generated (each one is the host-service.js child):
SIG COREFILE EXE SIZE
SIGABRT truncated /nix/store/.../superset-1.9.6/.../superset 1.2 GB
SIGABRT truncated /nix/store/.../superset-1.9.9/.../superset 1.2 GB
SIGABRT truncated /nix/store/.../superset-1.11.0/.../superset 1.2 GB
SIGABRT truncated /nix/store/.../superset-1.11.1/.../superset 1.2 GB
Command line is consistently superset .../resources/app.asar/dist/main/host-service.js. Stack frames are stripped in the bundled binary (#0 0x… n/a (n/a + 0x0)), so the dumps mostly confirm "V8 abort()".
Renderer console at the same time:
"Failed to kill removed terminal session [object Object]"
"[useV2PresetExecution] Failed to execute preset: TRPCClientError: Failed to fetch (127.0.0.1:43681)"
"Uncaught (in promise) TRPCClientError: Failed to fetch (127.0.0.1:43681)" × N
Workaround for Trigger 1
In ~/.superset/host/<org-id>/host.db and ~/.superset/tanstack-db.sqlite:
-- host.db: terminal_sessions had 130+ rows. Even rows with status='disposed'
-- and ended_at IS NOT NULL still trigger "respawning lost session" on startup —
-- only DELETE prevents respawn (marking disposed is not enough).
DELETE FROM terminal_sessions;-- tanstack-db.sqlite: applied_tx was 234 MB (348,998 rows) out of 247 MB total.
-- Replaying it at startup is what blows past V8's heap limit before anything else.
DELETE FROM applied_tx;
VACUUM;After both: cold-start OOM stops; host-service launches cleanly, listens on its tRPC port, terminals open. Then Trigger 2 takes over after 2–3 min of use.
What I ruled out
- Not an OS-level OOM — kernel never invoked OOM-killer, plenty of free RAM (48 GiB available at crash). SIGABRT is V8's own internal bailout.
- Not the Claude binary inside the terminal — repros with Claude Code 2.1.140 (no
anthropics/claude-code#45931exposure) and with Claude 2.1.149 alike. - Not a 1.11 regression — repros on 1.9.6 (pre PR refactor(desktop): couple host-service to Electron, drop adoption #4739 host-service-coupling refactor) just as readily.
- Not solved by [Bug] Renderer process memory leak → V8 GC death spiral (130%+ CPU after ~60 min) #3049's renderer-side fixes — that landed for the renderer; host-service has its own accumulation path.
- Not solved by capping cold restore at 20 entries (per [Bug] Renderer process memory leak → V8 GC death spiral (130%+ CPU after ~60 min) #3049 closing comment) — Trigger 2 happens even with 0 sessions to restore.
Hypothesis
Trigger 2 looks like classic IPC back-pressure: host-service produces structured-clone payloads (terminal output forwarded via Electron IPC) faster than the renderer drains them. The pipe buffer (~64 KB on Linux) fills, write() blocks, and queued outbound work continues to accumulate inside the V8 heap because no drain event is being awaited. Heap grows until V8 aborts.
Possibly related: the renderer-side fixes in #3049 reduced consumer pressure. The producer (host-service) may still need a writable-side back-pressure cap or await stream.drain() on its IPC channel.
A --max-old-space-size=8192 on the host-service child would buy users more time before the abort, but it's a band-aid. The right fix is to honor IPC back-pressure on host-service's writable side, or to cap the in-memory queue size and start dropping/coalescing terminal output above a threshold.
Environment
- OS: NixOS 26.05 (Yarara), Linux 7.0.5
- WM: i3 / X11
- Memory: 62 GiB total, 48 GiB available at crash
- Superset Desktop: repros on 1.9.6, 1.9.7, 1.9.9, 1.11.0, 1.11.1
- Electron: 40.10.0 (bundled)
- pty-daemon: 0.2.3 / 0.2.4
- Install: Nix derivation under
/nix/store/.../superset-<ver>/
Related issues
- [Bug] Renderer process memory leak → V8 GC death spiral (130%+ CPU after ~60 min) #3049 — same
Ineffective mark-compacts near heap limitcrash class on the renderer side, fixed there. Host-service appears to have its own version of the same bug. - [bug] terminals can still leak across different workspaces #4717, Main branch mixing terminal sessions across workspaces #4434 — terminal-session lifecycle / cross-workspace leakage.
- Update killed all terminals #4003 — "Update killed all terminals" — same user-visible symptom, less diagnostic depth.
- CPU Performance issues in V2 #4412 — 5 GB idle RAM with 12 workspaces.
- [bug] After Squirrel auto-update from 1.8.3 → 1.8.7, host-service.js daemon keeps running old code; v2 workspace UI shows "This workspace lives on a device that isn't reachable right now" #4252 — host-service runs stale code after Squirrel update (different bug, same component).
- [bug] Desktop terminal temporarily freezes after tab/workspace switch with Droid TUI #2968 — "Desktop terminal temporarily freezes after tab/workspace switch with Droid TUI" — same surface.
Activity
fix(host-service): solve #4868 — evict slow terminal WS consumers bef…
Kitenite commentedon May 23, 2026
Thanks for the report @otavio , this is very serious and debugging with this information
otavio commentedon May 23, 2026
I ended up reproducing it in a new environment in 1.11.1. Then I reverted to 1.9.9 in the new environment, and I couldn't reproduce it yet. In 1.11.1, when I started the new environment, I ended up with the OOM killer due to excessive memory usage, so I'm not sure if that's the same issue.
Kitenite commentedon May 23, 2026
that's good to know, it may already exist before and made worse by other terminal changes. either way adding backpressure should help just reproducing and seeing if i can patch on my end
fix(terminal): remove ACK output flow control to end PTY back-pressur…