Skip to content

SIGABRT crash during Electron process exit (ThreadSafeFunction cleanup race) #904

@Asukabot0

Description

@Asukabot0

Environment

  • node-pty version: 1.0.0 (via @electron/rebuild)
  • Electron: 35.7.5
  • Node.js: v24 (Electron embedded, NODE_MODULE_VERSION 141)
  • OS: macOS 26.3.1 (Darwin 25.3.0), ARM64 (Apple Silicon)
  • Reproduction context: Playwright E2E tests closing Electron windows

Description

When an Electron app using node-pty closes windows (or quits), macOS crash reports are generated with EXC_CRASH / SIGABRT (abort() called). The crash occurs during Node.js environment cleanup, not during normal operation. All E2E tests pass — the crash happens after test completion during window/process teardown.

This appears to be a race condition in the ThreadSafeFunction cleanup path.

Crash Stack (from macOS .ips report)

__pthread_kill
pthread_kill
abort
__abort_message
demangling_terminate_handler()
_objc_terminate()
std::__terminate(void (*)())
__cxxabiv1::failed_throw(__cxxabiv1::__cxa_exception*)
__cxa_throw
Napi::Error::ThrowAsJavaScriptException() const
void Napi::details::WrapVoidCallback<Napi::ThreadSafeFunction::CallJS(...)>(...)
Napi::ThreadSafeFunction::CallJS(napi_env__*, napi_value__*, void*, void*)
node::ThreadPoolWork::ScheduleWork()::'lambda'(uv_work_s*, int)::__invoke(uv_work_s*, int)
...
node::Environment::CleanupHandles()
node::Environment::RunCleanup()
node::FreeEnvironment(node::Environment*)

Analysis

The crash path shows:

  1. node::FreeEnvironment() begins tearing down the Node.js environment
  2. Environment::RunCleanup() / CleanupHandles() fires pending handle callbacks
  3. A node-pty ThreadSafeFunction::CallJS callback is invoked
  4. Inside the callback, Napi::Error::ThrowAsJavaScriptException() is called
  5. Since the environment is being destroyed, the exception cannot be handled
  6. This triggers __cxxabiv1::failed_throwstd::__terminateabort()

Reproduction

Occurs consistently (4-5 crashes per E2E test run) when Playwright closes Electron windows that have active PTY sessions. The PTY sessions are created via node-pty and attached to xterm.js terminals.

Steps:

  1. Create multiple Electron windows with PTY sessions via node-pty
  2. Close all windows rapidly (as Playwright does during E2E teardown)
  3. macOS crash reporter triggers for each affected process

Impact

  • No functional impact (all operations complete successfully before crash)
  • macOS crash report dialogs are disruptive during automated testing
  • Crash reports accumulate in ~/Library/Logs/DiagnosticReports/

Potential Fix Direction

The ThreadSafeFunction callback should check whether the environment is still alive before calling ThrowAsJavaScriptException(). Alternatively, the callback should be safely finalized/aborted during Environment::RunCleanup() before it can fire.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions