Problem
On macOS, the CLI force-exit backstop can stop the telemetry flush that runs at exit. Data that the SDK has not sent yet is lost.
Cause
- In the
finally of packages/cli/src/cli.ts, scheduleForceExit() arms an unref'd 100 ms timer that calls process.exit() (packages/cli/src/lib/force-exit.ts). It runs only on darwin.
- When the command's work drains,
beforeExit fires. createBeforeExitHandler() in packages/cli/src/lib/telemetry.ts ends the session and starts client.flush(3000).
- The flush opens HTTP requests. These keep the event loop alive, so the unref'd timer can fire.
- The timer calls
process.exit() before the flush completes. The flush is cut off.
The comment at the call site says the timer is a no-op on clean exits. That is true only until beforeExit starts new I/O. After that, the flush itself is the handle that keeps the loop alive.
Impact
Any data still in the SDK buffer when a command ends can be lost on macOS. For example:
- Metrics: the SDK buffers them for up to 5 s, so the exit flush is often the only send.
- Session updates from
Sentry.endSession() in the beforeExit handler.
- Client reports and runtime metrics that are sent at shutdown.
Evidence
The Snake leaderboard (#1437) found this problem. A snake.score metric was lost every time the player quit within about 5 s of game over. A trace log showed that the beforeExit flush started and the process exited before the request completed. #1437 works around this only for the score: it calls client.flush() when the game ends (packages/cli/src/lib/games/score.ts).
I saw the loss only for metrics. The impact on sessions and client reports comes from reading the code. I did not reproduce it.
Possible fixes
- Let the telemetry flush delay the force exit. For example,
beforeExit could clear or postpone the timer until flush() settles, with a maximum wait.
- Flush telemetry in the
finally of cli.ts before scheduleForceExit(), and keep the existing 3 s limit.
- Arm the timer only after the telemetry flush settles.
Any fix must keep the protection from #1237 and #833 against processes that do not exit.
Problem
On macOS, the CLI force-exit backstop can stop the telemetry flush that runs at exit. Data that the SDK has not sent yet is lost.
Cause
finallyofpackages/cli/src/cli.ts,scheduleForceExit()arms an unref'd 100 ms timer that callsprocess.exit()(packages/cli/src/lib/force-exit.ts). It runs only on darwin.beforeExitfires.createBeforeExitHandler()inpackages/cli/src/lib/telemetry.tsends the session and startsclient.flush(3000).process.exit()before the flush completes. The flush is cut off.The comment at the call site says the timer is a no-op on clean exits. That is true only until
beforeExitstarts new I/O. After that, the flush itself is the handle that keeps the loop alive.Impact
Any data still in the SDK buffer when a command ends can be lost on macOS. For example:
Sentry.endSession()in thebeforeExithandler.Evidence
The Snake leaderboard (#1437) found this problem. A
snake.scoremetric was lost every time the player quit within about 5 s of game over. A trace log showed that thebeforeExitflush started and the process exited before the request completed. #1437 works around this only for the score: it callsclient.flush()when the game ends (packages/cli/src/lib/games/score.ts).I saw the loss only for metrics. The impact on sessions and client reports comes from reading the code. I did not reproduce it.
Possible fixes
beforeExitcould clear or postpone the timer untilflush()settles, with a maximum wait.finallyofcli.tsbeforescheduleForceExit(), and keep the existing 3 s limit.Any fix must keep the protection from #1237 and #833 against processes that do not exit.