You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[node] SIGSEGV (exit 139) on linux-x64-musl under concurrent fetch/text() load; event loop stalls before the crash (0.14.5) #570
A long-running scraper using impit 0.14.5 (npm, impit-linux-x64-musl) dies with SIGSEGV (exit code 139) in roughly 6.5% of production runs. The crash is preceded by a long event-loop stall: our 45-second heartbeat timer stops firing 60–100+ seconds before the process dies, while the container stays alive. No JS exception is thrown and nothing reaches stderr, so the crash cannot be observed from inside the process.
This looks like a relative of #498 (fixed in js-0.14.3, exit 132/133 on concurrent body-read failures). Upgrading 0.14.1 → 0.14.5 cut our orphaned-run rate from ~14% to ~6.5%, but this second crash mode remains, now as a plain segfault.
Linux x64 (Apify platform). The same workload on darwin-arm64 has not reproduced the crash.
Usage pattern
One long-lived new Impit({ browser: 'firefox', proxyUrl, timeout: 45_000, http3: false, ignoreTlsErrors: true }) per run; proxyUrl is a rotating residential proxy with per-session credentials.
Additional short-lived instances are created for retries (same options, fresh proxy session).
Up to 5 concurrent impit.fetch() + await response.text() on the shared instance at peak; the rest of the pipeline is sequential.
Observed behavior across one week of production runs
232 runs, 15 ended in a hard kill with no graceful end: typical signature is normal progress, then timers stop firing, then SIGSEGV. One run died 115 s after start without ever reaching its first 45 s heartbeat.
Exit code is 139. Nothing on stderr, no abort message, no V8 check failure text (unlike the pre-0.14.3 crash, which printed Check failed: node->IsInUse()).
Crashes occur across different inputs; re-running a crashed input succeeds, so it is a race, not data-dependent.
What we don't have
No core dump (platform containers) and no minimal reproduction yet, since it needs a flaky proxy to trigger. Happy to run a debug build or set env flags on a subset of production runs if that helps capture a backtrace.
As a mitigation we are serializing response.text() calls so no two body reads can fail in the same instant; too early to say whether that eliminates it.
Update after more digging: the dominant cause of our orphaned runs was on our side, not in impit. Reproducing a crashed run's input as an owner run gave us readable logs, and the process was dying of V8 heap exhaustion (FATAL ERROR: Reached heap limit): we fed multi-megabyte, script-heavy publisher pages to jsdom in a container with a ~255 MB heap. The long GC pauses on the way down also explain the event-loop stalls we described. Stripping scripts/styles before the DOM parse fixed it; the same input now completes on the same container size.
Two things may still be of interest here. The timeout option does not appear to bound body streaming: a page that keeps streaming lets response.text() buffer indefinitely (we now read through response.body with a byte cap and cancel the reader). And our original exit code was 139 rather than the 134 a V8 heap abort usually produces, so a native component in the crash path is possible but unproven. Happy for this to be closed if nothing actionable remains on your side; if we see segfaults persist now that memory is bounded, I'll reopen with fresh evidence.
Activity
muhamed-didovic commentedon Oct 14, 2026
Update after more digging: the dominant cause of our orphaned runs was on our side, not in impit. Reproducing a crashed run's input as an owner run gave us readable logs, and the process was dying of V8 heap exhaustion (
FATAL ERROR: Reached heap limit): we fed multi-megabyte, script-heavy publisher pages to jsdom in a container with a ~255 MB heap. The long GC pauses on the way down also explain the event-loop stalls we described. Stripping scripts/styles before the DOM parse fixed it; the same input now completes on the same container size.Two things may still be of interest here. The
timeoutoption does not appear to bound body streaming: a page that keeps streaming letsresponse.text()buffer indefinitely (we now read throughresponse.bodywith a byte cap and cancel the reader). And our original exit code was 139 rather than the 134 a V8 heap abort usually produces, so a native component in the crash path is possible but unproven. Happy for this to be closed if nothing actionable remains on your side; if we see segfaults persist now that memory is bounded, I'll reopen with fresh evidence.