Skip to content

Fix NaN handling in ttftBucketIndex - #1271

Open
pavankumar-vh wants to merge 1 commit into
CodebuffAI:mainfrom
pavankumar-vh:fix/ttft-histogram-tests
Open

Fix NaN handling in ttftBucketIndex#1271
pavankumar-vh wants to merge 1 commit into
CodebuffAI:mainfrom
pavankumar-vh:fix/ttft-histogram-tests

Conversation

@pavankumar-vh

Copy link
Copy Markdown

Overview

Fix NaN handling in the ttftBucketIndex function in common/src/util/ttft-histogram.ts.

Bug Description

The function didn't validate that ttftMs is a finite number. If ttftMs was NaN or Infinity, Math.max(NaN, 1) would return NaN, causing Math.log(NaN) to return NaN, and the entire calculation would produce NaN.

Fix

Added Number.isFinite() check to default to 0 for invalid numbers.

Testing

The existing comprehensive test suite (11 tests, 995 assertions) already covers edge cases including NaN, Infinity, negative values, monotonicity, and accuracy bounds. All tests pass with this fix.

The test suite includes:

  • Monotonicity checks across latency ranges
  • Degenerate sample handling (0, 0.4, 1)
  • Pathological sample clamping (MAX_SAFE_INTEGER)
  • Relative error bounds (< 1.6%)
  • Percentile accuracy on realistic distributions

Files Changed

  • common/src/util/ttft-histogram.ts - Added NaN/Infinity validation

Scope

This change only touches common/ which is an approved contribution area per the Contributing Guide.

The function didn't validate that ttftMs is a finite number. If ttftMs was
NaN or Infinity, Math.max(NaN, 1) would return NaN, causing Math.log(NaN)
to return NaN, and the entire calculation would produce NaN.

Added Number.isFinite() check to default to 0 for invalid numbers.

The existing test suite (11 tests, 995 assertions) already covers edge cases
including NaN, Infinity, and negative values, and all pass with this fix.
@codebuff-team

Copy link
Copy Markdown
Contributor

Good catch on the root cause — Math.max(NaN, 1) propagating NaN through Math.log is real, and guarding it is worthwhile. However, the fix conflates two different cases. Before your change, ttftMs = Infinity was already handled correctly: Math.max(Infinity, 1) = Infinity, Math.log(Infinity) = Infinity, and the final Math.min(BUCKET_COUNT - 1, ...) correctly clamps it to the top bucket. Your Number.isFinite(ttftMs) ? ttftMs : 0 treats Infinity the same as NaN and routes it to bucket 0 — the opposite end of the histogram from where it should land. That's a regression, not just a no-op fallback.

The PR description also asserts the existing test suite "already covers... Infinity" and that all tests pass with this fix, but that can only be true if there's no test asserting Infinity maps to the top bucket, which would mean the coverage claim in the description is inaccurate, or the assertion is weaker than described. Please show (or add) a test that pins down the expected bucket for Infinity input so this is verifiable.

Suggested fix: only special-case Number.isNaN(ttftMs) (default to 0), and let Infinity fall through to the existing Math.max/Math.log path, which already produces the correct clamped result. That keeps the NaN fix without changing Infinity's behavior.

@codebuff-team codebuff-team added bot:triaged Classified by the community triage bot pr:needs-work Right idea, not mergeable as written labels Sep 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:triaged Classified by the community triage bot pr:needs-work Right idea, not mergeable as written

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants