-
Notifications
You must be signed in to change notification settings - Fork 305
Description
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:
node::FreeEnvironment()begins tearing down the Node.js environmentEnvironment::RunCleanup()/CleanupHandles()fires pending handle callbacks- A node-pty
ThreadSafeFunction::CallJScallback is invoked - Inside the callback,
Napi::Error::ThrowAsJavaScriptException()is called - Since the environment is being destroyed, the exception cannot be handled
- This triggers
__cxxabiv1::failed_throw→std::__terminate→abort()
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:
- Create multiple Electron windows with PTY sessions via node-pty
- Close all windows rapidly (as Playwright does during E2E teardown)
- 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.