Where does the bug appear (feature/product)?
Cursor IDE
Describe the Bug
Cursor’s built-in typescript-language-features extension contains a memory monitor (absent from upstream VS Code) that uses the pidusage npm package to periodically check tsserver’s RSS. On macOS, pidusage does this by spawning ps. Each successful ps invocation generates tens of thousands of syscalls as the OS processes the request. This runs in a continuous setTimeout loop for the lifetime of every Cursor window, resulting in a chronic, measurable syscall storm — particularly damaging on machines running endpoint security software like SentinelOne, which must inspect every spawned process.
Steps to Reproduce
- Open any TypeScript project in Cursor on macOS
- Run
sudo fs_usage -f exec | grep psin a terminal - Observe a steady stream of
psinvocations emanating fromCursor Helper (Plugin)
Expected Behavior
Expected Behavior
The memory monitor should not spawn external processes at all. If tsserver’s RSS is needed, it should be obtained via native macOS APIs (proc_pidinfo with PROC_PIDTASKINFO, or sysctl) — these return memory stats directly from the kernel with negligible syscall overhead and no child process creation.
At minimum, the monitor should only activate when the user has explicitly configured typescript.tsserver.maxMemory. In the default configuration, tsserver has no memory cap, so there is nothing to monitor — the feature should be dormant.
Operating System
MacOS
Version Information
Version: 3.5.38
VSCode Version: 1.105.1
Commit: 009bb5a3600dd98fe1c1f25798f767f686e14750
Date: 2026-05-26T21:32:06.537Z
Layout: editor
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 25.4.0
Additional Information
Root Cause
typescriptServiceClient (in the Cursor-modified typescript-language-features bundle) calls _startMemoryMonitor(server) on tsserver start. This method:
- Takes the tsserver process PIDs
- Enters a
setTimeoutloop callingpidusage(pids) pidusagespawnsps -o etime,pid,ppid,pcpu,rss,time -p <pids>on each iteration- Each
psinvocation triggers tens of thousands of syscalls at the OS level (primarilyclose()calls as macOS’spsiterates file descriptors across the process table)
Only the memory (RSS) field from the result is ever used — pcpu, etime, and time are fetched and discarded. The monitor fires regardless of whether tsserver is anywhere near a memory limit, and regardless of whether the user has configured typescript.tsserver.maxMemory.
This is a Cursor-specific addition — _startMemoryMonitor and the pidusage dependency do not exist in the upstream VS Code 1.105 TypeScript extension.
Impact
- Tens of thousands of syscalls per second sustained across every Cursor session on macOS
- On machines running endpoint security software (SentinelOne, CrowdStrike, etc.), each
psspawn triggers a security scan of the child process, compounding the overhead significantly - Elevates
Cursor Helper (Plugin)CPU% visible in Activity Monitor - Wakes security daemons (
endpointsecurity,com.apple.xpc.proxy) on every spawn - Compounds with multiple tsserver instances across workspace windows
- Entirely wasted work: the monitor fires constantly even when tsserver memory usage is nominal
Workaround
Patch extension.js in the Cursor app bundle to replace the ps spawn with /bin/false, which exits immediately with code 1 — pidusage handles this gracefully as “no matching pid found” and the monitor silently no-ops. Language features are unaffected; only the memory warning dialog becomes inert.
sudo sed -i '' 's|i("ps",l,|i("/bin/false",[],|g' \
/Applications/Cursor.app/Contents/Resources/app/extensions/typescript-language-features/dist/extension.js
Requires a Cursor restart. Must be reapplied after Cursor updates.
Suggested Fixes
-
Gate the monitor — only call
_startMemoryMonitorif the user has explicitly settypescript.tsserver.maxMemory. Most users never configure this, so the monitor is pure overhead for the majority of installs. -
Use native macOS process info — RSS for a child PID is available via
proc_pidinfo(pid, PROC_PIDTASKINFO, ...)without spawning any subprocess. Zero syscall overhead compared tops. -
Increase the poll interval substantially — if
psmust be used, polling infrequently (e.g. every 30–60 seconds) rather than in a tight loop would reduce the impact to acceptable levels.
Does this stop you from using Cursor
No - Cursor works, but with this issue