Terminal Segmentation Faults Create Zombie Processes

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

From the terminal, if I run a program (or Cursor runs a terminal command while testing code changes) that seg-faults, I end up with Zombie processes and I’m unable to kill them. It’s like the signal handlers for the child process that died are not being properly handled by the parent processes.

Looking the which processes are still running, I often the failed command + a cursor wrapper of some kind.

Steps to Reproduce

From Cursor Terminal

  1. Run a program that seg-faults from the terminal. The terminal just hangs.

To compare:
2. Run the same program from a standard shell and it’ll segfault with an error.

Operating System

MacOS

Version Information

Version: 2.6.21
VSCode Version: 1.105.1
Commit: fea2f546c979a0a4ad1deab23552a43568807590
Date: 2026-03-21T22:09:10.098Z
Build Type: Stable
Release Track: Default
Electron: 39.8.1
Chromium: 142.0.7444.265
Node.js: 22.22.1
V8: 14.2.231.22-electron.0
OS: Darwin arm64 23.3.0

Additional Information

It seems like the only way to remove the Zombie processes is to restart the application.

Does this stop you from using Cursor

No - Cursor works, but with this issue

Hey, thanks for the detailed report. Good repro steps and process info.

This looks like a gap in how the integrated terminal handles child process signals on macOS, specifically around SIGCHLD reaping when a grandchild segfaults inside the pty session.

A couple of quick questions to help us narrow it down:

  1. Which shell are you using in Cursor’s terminal, zsh, bash, or something else?
  2. What kind of program is segfaulting, C/C++ or something else? If you can share a minimal repro, even a trivial C program that does *(int*)0 = 0;, that’d help.

As a workaround for now, you can kill zombie processes from an external terminal with kill -9 <parent_pid> (the shell PID, not the zombie itself). That should clean up the zombie without restarting Cursor.

I’ve flagged this with the team. No ETA yet, but your report helps us prioritize it.

  1. I have Cursor configured with zsh as my terminal shell.
  2. Demo

Save this to a file

#include <stdio.h>

int main() {
int *ptr = NULL; // pointer that points to nothing
*ptr = 42; // ❌ writing to NULL -> segfault
printf("This will never print\n");
return 0;
}

Build the file:

cc segfault.c -o segfault

Run this inside the Cursor terminal:
./segfault

Reaping Process Workaround
I’ve tried the workaround to kill the parent process id…

  1. Getting the parent id of the zombie process

user:~/work/segfault-demo$ ps aux -o ppid= | grep demo
user 27306 0.0 0.0 408626896 1360 s074 U+ 1:18PM 0:00.00 grep demo 61347
user 26760 0.0 0.0 407971424 400 ?? UE 1:18PM 0:00.00 ./segfault-demo 36175

  1. Killed the parent id:
    user:~/work/segfault-demo$ sudo kill -9 36175

  2. Is is killed? It looks like it’s still orphaned and now has root process id as it’s parent and running kill -9 still fails to kill it after this….

user:~/work/segfault-demo$ ps aux -o ppid= | grep demo
user 29043 0.0 0.0 408499936 1120 s074 R+ 1:19PM 0:00.00 grep demo 61347
user 26760 0.0 0.0 407971424 400 ?? UE 1:18PM 0:00.00 ./segfault-demo 1

Great repro, super helpful.

On the workaround: the zombie moving under PID 1 after you kill -9 the parent shell is expected on macOS. A zombie process is already “dead” meaning its code has finished, but the process table entry stays until the parent calls wait(). When you kill the parent, the zombie gets reparented to init PID 1, and it will stay in the table until init reaps it, or until you restart Cursor.

The most reliable workaround right now is to close and reopen Cursor. Not ideal, but it reliably clears all zombies.

The root cause is most likely how node-pty handles SIGCHLD from grandchild processes in a pty session on macOS. No specific timeline yet, but a report with a repro this good definitely helps with prioritization.