Skip to content

bug(pipes): serialize NaN/Inf as null instead of opaque 500 "json: unsupported value: NaN" #365

Description

@EricAndrechek

Area: pipes · api — bug (opaque error / robustness) · found via WaveHouse-Stats dogfooding

Expected: A pipe/query aggregate that yields NaN or ±Inf (e.g. medianIf/avgIf, or a division over an empty in-window set) serializes as null, or returns a clear error naming the offending column.
Actual: POST /v1/pipes/<name> returns 500 {"error":"json: unsupported value: NaN"}. Go's encoding/json can't marshal non-finite floats, so the whole response fails at internal/api/pipes.go:200 (json.Marshal(rows) → err → writeJSONError(500, err.Error())). The message blames "json", not the column/window that produced NaN, and it fails the whole pipe — one empty-window median blanks an entire dashboard card. The same unguarded json.Marshal(rows) path is in internal/api/structured_query.go:208.
Impact: A 7-day range with no matching rows 500'd gh_issue_stats, so every issue tile rendered "NaN". Consumers must defensively guard every aggregate with if(countIf(cond) > 0, agg, NULL) — ClickHouse's nanToNull() does not exist as of CH 26.6, so there's no one-liner shortcut.

Scope: sanitize non-finite floats at the result-marshal boundary (pipes + structured query). Distinct from #271 (that's status-code/retryable classification of CH query errors; this is serialization of a successful result).

Related: #271


From WaveHouse-Stats WAVEHOUSE-FEEDBACK.md dogfooding (WaveHouse-Stats#51, logged 2026-07-02); validated by code-read against 029fe1f on 2026-07-02.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/apiHTTP handlers, routing, middlewarearea/pipesNamed query pipesbugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions