Skip to content

Client.flush() leaks log/metric flush timers when client has no transport #20206

@sbs44

Description

@sbs44

Is there an existing issue for this?

How do you use Sentry?

Sentry Saas (sentry.io)

Which SDK are you using?

@sentry/deno

SDK Version

10.48.0 (bug was introduced alongside setupWeightBasedFlushing)

Framework Version

No response

Link to Sentry event

No response

Reproduction Example/SDK Setup

import * as Sentry from '@sentry/deno';

Sentry.init({
  dsn: '', // no DSN... _transport is undefined
  enableLogs: true,
});

Sentry.logger.info('hello');
await Sentry.flush(2000);
// Expected: all pending work drained, no leaked handles
// Actual: a setTimeout from setupWeightBasedFlushing is still pending

Minimal reproduction as a Deno test (fails with "Leaks detected: timers were started in this test, but never completed"):

// repro.test.ts
// deno test --allow-env --allow-net repro.test.ts
import * as Sentry from 'npm:@sentry/deno@10.48.0';

Deno.test('flush does not clear weight-based flush timer without transport', async () => {
  Sentry.init({ dsn: '', enableLogs: true });
  Sentry.logger.info('hello');
  await Sentry.flush(2000);
});

Steps to Reproduce

  1. Initialize a Sentry client with no DSN (or any configuration that leaves _transport undefined), with enableLogs: true or any code path that records a metric via Sentry.metrics.*.
  2. Capture at least one log or metric so setupWeightBasedFlushing starts its 5-second idle setTimeout.
  3. Call Sentry.flush() (or client.flush()).
  4. Observe that the setTimeout handle is still pending after flush() resolves.

Expected Result

Client.flush() drains the log and metric buffers and clears the weight-based flusher's idle timer, regardless of whether a transport is configured. On runtimes with strict handle/op tracking (Deno test runner, Node --trace-uncaught style harnesses), the process or test has no lingering timer after flush() returns.

Actual Result

Client.flush() returns immediately when !this._transport, without emitting the 'flush' event. The listener installed by setupWeightBasedFlushing (packages/core/src/client.ts) is the only thing that clears flushTimeout, so the idle timer keeps running until the 5-second DEFAULT_FLUSH_INTERVAL elapses (or indefinitely, in environments where the timer is unref'd but the test sanitizer still tracks it).

Deno's test sanitizer surfaces this as:

error: Leaks detected:
  - 2 timers were started in this test, but never completed.
    at setTimeout (node:timers:14:10)
    at .../@sentry/core/10.48.0/build/esm/client.js:108:9
    at uniqueCallback (.../@sentry/core/10.48.0/build/esm/client.js:551:41)
    at DenoClient.emit (.../@sentry/core/10.48.0/build/esm/client.js:572:17)

Additional Context

  • Root cause is the early return in Client.flush():

    public async flush(timeout?: number): PromiseLike<boolean> {
      const transport = this._transport;
      if (!transport) {
        return true;            // <-- returns before emitting 'flush'
      }
      this.emit('flush');
      // ...
    }

    The setupWeightBasedFlushing listeners registered on 'flushLogs'/'flushMetrics' (triggered via the 'flush' hook) are what clearTimeout(flushTimeout)... so skipping the emit leaves the timer permanently armed.

  • Affects any environment that legitimately runs without a DSN (unit tests, CI smoke tests, development mode), and is most visible on Deno where the test runner's op sanitizer fails any test with a lingering timer.

  • We originally hit this in Supabase Deno edge function tests where the shared harness calls Sentry.init({ dsn: '', enableLogs: true }) and records metrics per request, then calls Sentry.flush(2000) in the request finalizer.

  • Fix is trivial... move this.emit('flush') above the !transport early return. PR incoming.

Priority

React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it.

Metadata

Metadata

Assignees

No one assigned
    No fields configured for issues without a type.

    Projects

    Status

    Waiting for: Product Owner

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions