redis/node-redis · error · Error

All statistics values must be non-negative

Error message

All statistics values must be non-negative

What it means

CacheStats' constructor (reached via CacheStats.of) enforces that all six counters (hitCount, missCount, loadSuccessCount, loadFailureCount, totalLoadTime, evictionCount) are >= 0. A negative value would corrupt every derived rate (hitRate, missRate, loadFailureRate) and break the Caffeine-style stats contract, so the constructor rejects it outright.

Source

Thrown at packages/client/lib/client/cache.ts:51

   * Creates a new CacheStats instance with the specified statistics.
   */
  private constructor(
    public readonly hitCount: number,
    public readonly missCount: number,
    public readonly loadSuccessCount: number,
    public readonly loadFailureCount: number,
    public readonly totalLoadTime: number,
    public readonly evictionCount: number
  ) {
    if (
      hitCount < 0 ||
      missCount < 0 ||
      loadSuccessCount < 0 ||
      loadFailureCount < 0 ||
      totalLoadTime < 0 ||
      evictionCount < 0
    ) {
      throw new Error('All statistics values must be non-negative');
    }
  }

  /**
   * Creates a new CacheStats instance with the specified statistics.
   *
   * @param hitCount - Number of cache hits
   * @param missCount - Number of cache misses
   * @param loadSuccessCount - Number of successful cache loads
   * @param loadFailureCount - Number of failed cache loads
   * @param totalLoadTime - Total load time in milliseconds
   * @param evictionCount - Number of cache evictions
   */
  static of(
    hitCount = 0,
    missCount = 0,
    loadSuccessCount = 0,
    loadFailureCount = 0,

View on GitHub (pinned to bb5beb5657)

Solutions

  1. Ensure all record*(value) calls receive non-negative values (clamp with Math.max(0, value)).
  2. If authoring a custom StatsCounter, guard recordHits/recordMisses/recordEvictions to reject negative counts and recordLoadSuccess/recordLoadFailure to reject negative times.
  3. Audit the call sites that construct CacheStats directly (most users should only read snapshots).

Example fix

// before
counter.recordLoadSuccess(negativeElapsed);

// after
counter.recordLoadSuccess(Math.max(0, negativeElapsed));
Defensive patterns

Strategy: validation

Validate before calling

function safeRecord(counter, fn, value) {
  if (typeof value === 'number' && value >= 0) fn.call(counter, value);
}

Type guard

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

Prevention

When it happens

Trigger: Calling CacheStats.of(...) with a negative argument directly; a StatsCounter.recordX(-n) producing a negative running total whose snapshot is then built; an arithmetic underflow in a custom StatsCounter; subtraction (minus) already clamps with Math.max(0,...), so hitting this implies a non-clamped code path.

Common situations: A custom StatsCounter that doesn't guard against negative inputs; concurrent decrement logic; test fixtures passing negative numbers; a bug where recordLoadFailure is called with a negative elapsed time.

Related errors


AI-assisted analysis of redis/node-redis@bb5beb5657 (2026-08-03). Data as JSON: /data/errors/e4e61100aec4b8f8.json. Report an issue: GitHub.