-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Open
Labels
Description
Is there an existing issue for this?
- I have checked for existing issues https://github.com/getsentry/sentry-javascript/issues
- I have reviewed the documentation https://docs.sentry.io/
- I am using the latest SDK release https://github.com/getsentry/sentry-javascript/releases
How do you use Sentry?
Sentry SaaS (sentry.io)
Which SDK are you using?
@sentry/node
SDK Version
10.36.0
Framework Version
Bun 1.3.3 / Node.js 22
Link to Sentry event
No response
Reproduction Example/SDK Setup
import * as Sentry from "@sentry/node";
const client = Sentry.init({
dsn: "https://[email protected]/123",
enabled: true,
defaultIntegrations: false,
});
// This will block for 3 seconds even when there's nothing to send
await client?.flush(3000);Steps to Reproduce
- Initialize Sentry in a CLI application
- Call
client.flush(3000)before exiting - Observe the process hangs for ~3 seconds even when there's nothing to flush
Expected Result
flush(timeout) should:
- Return immediately if there's nothing to process (
_numProcessing === 0) - Use
unref()on internal timers so the process can exit naturally when work is complete
Actual Result
The process blocks for the full timeout duration because:
-
_isClientDoneProcessing()inclient.jschecks_numProcessingafter the firstsetTimeout, not before:async _isClientDoneProcessing(timeout) { let ticked = 0; while (!timeout || ticked < timeout) { await new Promise(resolve => setTimeout(resolve, 1)); // Always waits first if (!this._numProcessing) { return true; } ticked++; } return false; }
-
The
setTimeoutcalls don't use.unref(), keeping the Node.js event loop alive:client.jsline 650:setTimeout(resolve, 1)- polling looppromisebuffer.jsline 72:setTimeout(() => resolve(false), timeout)- drain timeout
Other parts of the codebase already use .unref() correctly (e.g., httpServerIntegration.js line 234).
Suggested Fix
-
Add early exit check in
_isClientDoneProcessing():async _isClientDoneProcessing(timeout) { if (!this._numProcessing) { return true; // Early exit } // ... rest of polling loop }
-
Add
.unref()to timers with browser-safe check:await new Promise(resolve => { const t = setTimeout(resolve, 1); if (typeof t !== 'number' && t.unref) t.unref(); });