Skip to content

fix(client): ignore NaN in setDownloadProgress instead of storing it#5070

Open
jeffrey701 wants to merge 1 commit into
phase-rs:mainfrom
jeffrey701:fix/download-progress-nan-guard
Open

fix(client): ignore NaN in setDownloadProgress instead of storing it#5070
jeffrey701 wants to merge 1 commit into
phase-rs:mainfrom
jeffrey701:fix/download-progress-nan-guard

Conversation

@jeffrey701

Copy link
Copy Markdown
Contributor

Problem

setDownloadProgress (client/src/pwa/updateStatus.ts) documents a 0–100 clamp, but NaN escapes it: Math.max/Math.min propagate NaN, and the downloadProgress === clamped change guard never trips (NaN !== NaN), so NaN is stored and pushed to listeners. BuildBadge then renders "NaN%" and style={{ width: "NaN%" }} (invalid CSS), and each subsequent NaN re-notifies every listener.

NaN is exactly the receivedBytes / totalBytes * 100 result when a download reports no content-length — tauriUpdater.ts reads event.data.contentLength ?? 0, so totalBytes can be 0. That caller already had to add a totalBytes > 0 workaround; the utility should defend its own documented contract rather than rely on every caller to guard.

Fix

A Number.isNaN(value) early return that keeps the last valid progress. Every finite value and ±Infinity still clamp correctly (Infinity→100, -Infinity→0) — only NaN is rejected.

Tests

Adds the module's first test: the clamp/rounding, ±Infinity, and the NaN case (which fails without the guard — discriminating, seeded with a known value so it is immune to singleton state). tsc -b --noEmit and eslint clean.

Self-contained: only updateStatus.ts + a new test; no engine/Rust/protocol changes.

Model: claude-opus-4-8

setDownloadProgress documents a 0–100 clamp, but NaN slips through:
Math.max/Math.min propagate NaN, and the `downloadProgress === clamped` change
guard never fires (NaN !== NaN), so NaN is stored and pushed to listeners. The
BuildBadge UI then renders "NaN%" and style={{ width: "NaN%" }} (invalid CSS),
and every subsequent NaN re-notifies. NaN is exactly the receivedBytes /
totalBytes result when a download reports no content-length (totalBytes === 0),
which is why the Tauri updater caller already had to add a totalBytes > 0
workaround — the utility should defend its own contract.

Add a Number.isNaN early return so the last valid progress is kept. ±Infinity
still clamp correctly to 100 / 0. Adds the module's first test, covering the
clamp, ±Infinity, and the NaN case (which fails without the guard).
@jeffrey701 jeffrey701 requested a review from matthewevans as a code owner July 4, 2026 05:13
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@matthewevans matthewevans added the defer-fe Frontend/client/UI PR deferred to Matt's direct review label Jul 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

defer-fe Frontend/client/UI PR deferred to Matt's direct review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants