mongodb/node-mongodb-native · error · MongoInvalidArgumentError

Cannot create a Timeout with a negative duration

Error message

Cannot create a Timeout with a negative duration

What it means

Thrown by the @internal Timeout class constructor when the requested duration is a negative number. Durations of 0 are allowed (used for immediately-rejecting timeouts), but negative durations are nonsensical and rejected up front. It is a MongoInvalidArgumentError.

Source

Thrown at src/timeout.ts:64

    if (this.duration === 0) return Infinity;
    return this.start + this.duration - Math.trunc(performance.now());
  }

  get timeElapsed(): number {
    return Math.trunc(performance.now()) - this.start;
  }

  /** Create a new timeout that expires in `duration` ms */
  private constructor(
    executor: Executor = () => null,
    options?: { duration: number; unref?: true; rejection?: Error }
  ) {
    const duration = options?.duration ?? 0;
    const unref = !!options?.unref;
    const rejection = options?.rejection;

    if (duration < 0) {
      throw new MongoInvalidArgumentError('Cannot create a Timeout with a negative duration');
    }

    let reject!: Reject;
    super((_, promiseReject) => {
      reject = promiseReject;

      executor(noop, promiseReject);
    });

    this.duration = duration;
    this.start = Math.trunc(performance.now());

    if (rejection == null && this.duration > 0) {
      this.id = setTimeout(() => {
        this.ended = Math.trunc(performance.now());
        this.timedOut = true;
        reject(new TimeoutError(`Expired after ${duration}ms`, { duration }));
      }, this.duration);

View on GitHub (pinned to 3366c21a63)

Solutions

  1. If you are a driver maintainer, clamp duration to 0 when negative before constructing a Timeout, or use Timeout.reject() to fail fast.
  2. As a user, ensure timeoutMS values are non-negative and that you are not chaining operations after a timeout has already fired.
  3. Upgrade the driver: many negative-duration paths were hardened in recent releases; check HISTORY.md.

Example fix

// before (driver-internal pattern)
return new Timeout(undefined, { duration: remaining - slack }); // remaining < slack -> throws

// after
const dur = Math.max(0, remaining - slack);
return dur === 0 ? Timeout.reject(new MongoOperationTimeoutError('expired')) : Timeout.expires(dur);
Defensive patterns

Strategy: validation

Validate before calling

// internal: clamp durations before constructing a Timeout
const safeDuration = Math.max(0, requestedDuration);
if (safeDuration === 0) {
  return Timeout.reject(new MongoOperationTimeoutError('expired'));
}
return Timeout.expires(safeDuration);

Type guard

function isNonNegativeMs(v: unknown): v is number {
  return typeof v === 'number' && Number.isFinite(v) && v >= 0;
}

Prevention

When it happens

Trigger: Driver internals computing a remaining time that goes negative (e.g. timeoutMS already exceeded before a Timeout.expires() call) and passing it to the constructor. Not reachable through the public API directly under normal use.

Common situations: CSOT (Client-Side Operation Timeout) edge cases where the deadline has already passed by the time a new Timeout is constructed; bugs in timeout arithmetic; race between timeout expiry and a new operation start.

Related errors


AI-assisted analysis of mongodb/node-mongodb-native@3366c21a63 (2026-08-04). Data as JSON: /data/errors/d083d37a9d7e3eff.json. Report an issue: GitHub.