Fix: packaged app hangs on "Restoring session..." (bundle fsevents)#140
Open
matanelcohen wants to merge 2 commits into
Open
Fix: packaged app hangs on "Restoring session..." (bundle fsevents)#140matanelcohen wants to merge 2 commits into
matanelcohen wants to merge 2 commits into
Conversation
…on..." Packaged (dist) builds hung forever on the SessionLoading screen while `npm start` worked. chokidar's macOS FSEvents backend needs the native `fsevents` optionalDependency, which was present in dev node_modules but never bundled into the packaged app. Without it, chokidar (usePolling:false) falls back to one fs.watch fd per directory; on accounts with thousands of ~/.copilot session dirs that exhausts the fd limit (EMFILE: too many open files, watch), saturating the main event loop so renderer startup IPC stalls and isRestoring never clears. Finder/Dock launches inherit a 256 fd soft limit, compounding it. - forge.config.ts: copy fsevents in postPackage (like node-pty/better-sqlite3) - vite.main.config.ts: externalize fsevents for the runtime probe - src/main/utils/fsevents.ts: canUseNativeRecursiveWatch() probe - copilot/claude-code session watchers: fall back to bounded polling when fsevents is unavailable (defense-in-depth so a missing native module degrades instead of hanging) Verified on a real package: usePolling=false + 0 EMFILE with fsevents, graceful usePolling=true fallback without it; app restores its multi-pane session past the loading screen. 115 unit tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… quit Bundling fsevents (the EMFILE startup fix) introduced an FSEvents watcher whose N-API threadsafe function must be released while the libuv loop is alive. The watchers were only closed in window-all-closed, so on Cmd-Q / SIGTERM the Node env tore down with the FSEvents watcher still active and the app aborted (SIGABRT) on exit: fsevents.node :: fse_instance_destroy -> napi_release_threadsafe_function -> uv_mutex_lock -> abort (CrBrowserMain, during node::Stop / FreeEnvironment) fsevents' Native.stop (invoked by chokidar close()/stop()) releases the TSFN, so closing the watchers before teardown fixes it. - Extract shutdownResources(): idempotent teardown that closes all chokidar/fsevents watchers, kills PTYs, disposes monitors. - Gate before-quit: preventDefault, run shutdownResources (2s timeout guard), then re-issue app.quit() so normal teardown (will-quit + renderer beforeunload session-save) still runs — now abort-free. - window-all-closed routes through app.quit() (into the gated cleanup). - Handle SIGINT/SIGTERM/SIGHUP via app.quit() so external termination also cleans up instead of aborting. Verified on a real package: SIGTERM and Apple-Event (Cmd-Q) quits both exit cleanly with zero new crash reports. 115 unit tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Author
|
Follow-up commit With fsevents now bundled, the FSEvents watcher's N-API threadsafe function was being released during Node env teardown ( The watchers were only closed in
Verified on a real package: SIGTERM and Apple-Event (Cmd-Q) quits both exit cleanly with zero new crash reports. 115 unit tests pass. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #139
Problem
Packaged (dist) builds hang forever on the "Restoring session..." screen while
npm startworks. The session watchers usechokidar.watch(basePath, { usePolling: false, depth: 2 }); chokidar only uses the efficient single FSEvents watcher on macOS when the nativefseventsmodule loads.fseventsis present in dev but was not bundled into the packaged app, so chokidar fell back to onefs.watchfd per directory. On accounts with thousands of~/.copilotsession dirs (repro: 4,972) this exhausts the fd limit:The flood saturates the main event loop, the renderer's startup IPC stalls, and
isRestoringnever clears. Finder/Dock launches inherit a 256 fd soft limit, compounding it.Changes
fseventsinpostPackage(same mechanism asnode-pty/better-sqlite3).fseventsfor the runtime probe.canUseNativeRecursiveWatch().usePolling: truewhenfseventsis unavailable (defense-in-depth: degrade instead of hang).Verification (real package, macOS arm64)
fsevents:usePolling=false, 0 EMFILE, both watchers reachready, app restores the saved 4-pane session past the loading screen.fsevents(simulated by removing it from the package): gracefulusePolling=truefallback, no EMFILE flood, no hang.npm run test:unit→ 115/115 pass.