{"record":{"id":"e4e61100aec4b8f8","repo":"redis/node-redis","slug":"all-statistics-values-must-be-non-negative","errorCode":null,"errorMessage":"All statistics values must be non-negative","messagePattern":"All statistics values must be non-negative","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/client/lib/client/cache.ts","lineNumber":51,"sourceCode":"   * Creates a new CacheStats instance with the specified statistics.\n   */\n  private constructor(\n    public readonly hitCount: number,\n    public readonly missCount: number,\n    public readonly loadSuccessCount: number,\n    public readonly loadFailureCount: number,\n    public readonly totalLoadTime: number,\n    public readonly evictionCount: number\n  ) {\n    if (\n      hitCount < 0 ||\n      missCount < 0 ||\n      loadSuccessCount < 0 ||\n      loadFailureCount < 0 ||\n      totalLoadTime < 0 ||\n      evictionCount < 0\n    ) {\n      throw new Error('All statistics values must be non-negative');\n    }\n  }\n\n  /**\n   * Creates a new CacheStats instance with the specified statistics.\n   *\n   * @param hitCount - Number of cache hits\n   * @param missCount - Number of cache misses\n   * @param loadSuccessCount - Number of successful cache loads\n   * @param loadFailureCount - Number of failed cache loads\n   * @param totalLoadTime - Total load time in milliseconds\n   * @param evictionCount - Number of cache evictions\n   */\n  static of(\n    hitCount = 0,\n    missCount = 0,\n    loadSuccessCount = 0,\n    loadFailureCount = 0,","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/redis/node-redis/blob/90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58/packages/client/lib/client/cache.ts#L33-L69","documentation":"CacheStats is constructed with six counters (hitCount, missCount, loadSuccessCount, loadFailureCount, totalLoadTime, evictionCount). All must be non-negative because cache counters are monotonic by definition; the constructor rejects any negative value. Used to validate stats snapshots created via CacheStats.of(...).","triggerScenarios":"Calling CacheStats.of with a negative argument for any of the six counters; a differencing computation that subtracts in the wrong order; deserialization that lost sign information.","commonSituations":"Building a delta between two CacheStats snapshots by naive subtraction (a - b when b > a); passing through a NaN/undefined that became negative after coercion; incorrect manual stats aggregation.","solutions":["Use CacheStats.empty() or the factory defaults instead of constructing values yourself.","Clamp deltas with Math.max(0, x) before passing them in.","Fix the differencing logic to never go negative (counters are monotonic)."],"exampleFix":"// before\nCacheStats.of(a.hitCount - b.hitCount, a.missCount - b.missCount, ...);\n\n// after\nCacheStats.of(\n  Math.max(0, a.hitCount - b.hitCount),\n  Math.max(0, a.missCount - b.missCount),\n  ...\n);","handlingStrategy":"validation","validationCode":"function safeStatsOf(hit, miss, loadOk, loadFail, loadTime, evict) {\n  const vals = [hit, miss, loadOk, loadFail, loadTime, evict];\n  if (vals.some(v => typeof v !== 'number' || Number.isNaN(v) || v < 0)) {\n    throw new Error('All CacheStats values must be finite non-negative numbers');\n  }\n  return CacheStats.of(hit, miss, loadOk, loadFail, loadTime, evict);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never subtract CacheStats counters naively; clamp with Math.max(0, a - b).","Use CacheStats.empty() as the base rather than constructing zeros manually.","Guard against NaN/undefined flowing in from deserialization."],"tags":["cache","validation","statistics","cache-stats"],"backgroundTag":null,"analyzedSha":"90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58","analyzedAt":"2026-08-11T15:37:21.243Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}