Skip to content

sparkline() emits literal "undefined" for fractional value ranges (tick index can exceed array bounds) #3885

Description

@krishna3554

Summary

sparkline() indexes the 8-element ticks array with a value that can reach 8, yielding undefined for the peak data point. The output then contains the literal string "undefined" wherever the maximum (or values near it) lands. This is reachable from heroku dashboard metrics, which feed fractional (float) values into the function.

Location

const ticks = ['▁','▂','▃','▄','▅','▆','▇','█']   // indices 0..7

const f = Math.floor(lshift(max - min, 8) / (ticks.length - 1))   // lshift(n,8) = n*256
...
const value = ticks[Math.floor(lshift(validValue - min, 8) / f)]  // can be ticks[8]

Problem

The index math is not clamped. For range = max - min, f = Math.floor(range * 256 / 7); the peak value maps to Math.floor(range * 256 / f), which exceeds 7 whenever range * 256 is a small multiple of 8 but not of 7.

Concrete failing ranges (max - min):

range range*256 f max index result
0.03125 (=1/32) 8 1 8 undefined
0.0625 16 2 8 undefined
0.125 32 4 8 undefined
0.1875 48 6 8 undefined

With integer inputs this never happens (range*256 is a multiple of 256), which is why the existing unit tests pass — they only exercise integers. But src/commands/dashboard.ts builds sparkline points by summing router-latency metric samples (points[j] = (points[j] || 0) + element), and latency values are fractional milliseconds, so float ranges occur in practice.

Trigger / Reproduction

Static analysis finding — behavior derived from source at main (5118f40a); not confirmed by execution:

sparkline([0, 0.03125])
// → '▁' + ticks[Math.floor(256/1)] === '▁undefined'

and via the CLI: heroku dashboard --app <app> where the summed per-hour latency points happen to span one of the failing ranges — the rendered dashboard line shows undefined characters instead of blocks.

Expected Behavior

The tick index should be clamped to the last bucket, e.g.

const idx = Math.min(7, Math.floor(lshift(validValue - min, 8) / f))

so every valid value renders as a block character.

Actual Behavior

Out-of-range indices produce undefined, which stringifies into the returned sparkline.

Impact

Corrupted/ugly output in heroku dashboard for ordinary float-valued metrics; any other consumer of the exported sparkline() utility is affected identically. Purely cosmetic but user-visible and trivially reproducible once the value distribution matches.

Suggested Direction

Clamp the computed index to ticks.length - 1 (and add float-range unit tests such as [0, 1/32], [0, 0.0625]). Alternatively compute the bucket as Math.round((v - min) / range * 7) with an explicit guard for range === 0.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions