What
Every ordinary SELECT ("Table" result view) and KPI panel query streams via
JSONStringsEachRowWithProgress/JSONEachRowWithProgress
(src/net/ch-client.ts:1069). applyStreamLine()
(src/core/stream.ts:80-106) learns column names and types exclusively
from a {"meta":[...]} line:
if (json.meta) {
result.columns = json.meta.map((m) => ({ name: m.name, type: m.type }));
} else if (json.row) {
result.rows.push(result.columns.map((c) => row[c.name]));
}
ClickHouse only started emitting a meta line for these progress-bearing
NDJSON formats via ClickHouse GitHub PR #74181 ("JSONEachRowWithProgress
format will include meta, totals, and extremes"), merged 2025-01-06. On any
server predating that change, no meta line ever arrives, result.columns
stays [] for the whole response, and every row silently resolves to an
empty array — even though each {"row": {...}} line already carries the
values keyed by column name. No error is surfaced; the grid just renders
empty.
Confirmed on
Empirically confirmed live (not simulated) during the #585/ADR-0005
@clickhouse/client-web validation spike, against pinned Docker images:
24.8.14.39 (OSS)
24.8.14.10547.altinitystable
Both fail identically on the live 39-case precision corpus and the live
scenario suite (docs/evidence/585/compatibility-matrix.md,
results.json.precision) — using the current production transport, not
only the spike's official-client candidate: currentMatchesOfficial: true
(both agree with each other) while currentMatchesExpected: false (both
wrong) on every case.
The exact earliest ClickHouse minor that starts emitting the meta line is
not known — only 24.8.x (fails) and 26.3/26.6 (passes) were tested; the
25.x line was never exercised (see ADR-0005's "Limitations" section).
Why this wasn't caught earlier
This repo has no live-ClickHouse CI/e2e coverage in its normal test suite —
stream.ts's existing unit tests only exercise synthetic fixtures that
always supply a meta line first. The #585 spike is the first time this
exact production code path was pointed at a real, pinned pre-2025 ClickHouse
server and checked for correct values coming back.
This is not a defect introduced by, or specific to,
@clickhouse/client-web. ADR-0005 (Rejected) documents the current
transport and the official-client candidate reading back identical wrong
values on both 24.8 rows — see "This affects the current production code
equally" in the Decision section.
Impact
Anyone running SQL Browser against a ClickHouse server old enough to predate
the meta-line change gets silently empty result grids for ordinary queries,
with no error — likely to read as "the app is broken" rather than
"unsupported server version."
Proposed fix direction
Add a meta-less fallback to applyStreamLine(): when a row line arrives
and result.columns is still empty, derive column names from
Object.keys(row) instead of leaving columns empty. Column type isn't
recoverable from the row payload alone (only meta carries it), so
downstream consumers of column.type — KPI numeric/tuple parsing
(src/core/kpi.ts), chart axis/role detection (src/core/chart-data.ts),
variable-options validation (src/core/variable-options.ts), compact type
display (src/core/type-display.ts), and type-aware grid cell formatting —
need a defined degraded behavior (e.g. treat as unknown/string type) for
servers in this gap, or those features need to be documented as
reduced-fidelity below the meta-line floor.
Acceptance
Relates to
What
Every ordinary SELECT ("Table" result view) and KPI panel query streams via
JSONStringsEachRowWithProgress/JSONEachRowWithProgress(
src/net/ch-client.ts:1069).applyStreamLine()(
src/core/stream.ts:80-106) learns column names and types exclusivelyfrom a
{"meta":[...]}line:ClickHouse only started emitting a
metaline for these progress-bearingNDJSON formats via ClickHouse GitHub PR #74181 ("JSONEachRowWithProgress
format will include meta, totals, and extremes"), merged 2025-01-06. On any
server predating that change, no
metaline ever arrives,result.columnsstays
[]for the whole response, and every row silently resolves to anempty array — even though each
{"row": {...}}line already carries thevalues keyed by column name. No error is surfaced; the grid just renders
empty.
Confirmed on
Empirically confirmed live (not simulated) during the #585/ADR-0005
@clickhouse/client-webvalidation spike, against pinned Docker images:24.8.14.39(OSS)24.8.14.10547.altinitystableBoth fail identically on the live 39-case precision corpus and the live
scenario suite (
docs/evidence/585/compatibility-matrix.md,results.json.precision) — using the current production transport, notonly the spike's official-client candidate:
currentMatchesOfficial: true(both agree with each other) while
currentMatchesExpected: false(bothwrong) on every case.
The exact earliest ClickHouse minor that starts emitting the meta line is
not known — only 24.8.x (fails) and 26.3/26.6 (passes) were tested; the
25.x line was never exercised (see ADR-0005's "Limitations" section).
Why this wasn't caught earlier
This repo has no live-ClickHouse CI/e2e coverage in its normal test suite —
stream.ts's existing unit tests only exercise synthetic fixtures thatalways supply a
metaline first. The #585 spike is the first time thisexact production code path was pointed at a real, pinned pre-2025 ClickHouse
server and checked for correct values coming back.
This is not a defect introduced by, or specific to,
@clickhouse/client-web. ADR-0005 (Rejected) documents the currenttransport and the official-client candidate reading back identical wrong
values on both 24.8 rows — see "This affects the current production code
equally" in the Decision section.
Impact
Anyone running SQL Browser against a ClickHouse server old enough to predate
the meta-line change gets silently empty result grids for ordinary queries,
with no error — likely to read as "the app is broken" rather than
"unsupported server version."
Proposed fix direction
Add a meta-less fallback to
applyStreamLine(): when arowline arrivesand
result.columnsis still empty, derive column names fromObject.keys(row)instead of leavingcolumnsempty. Column type isn'trecoverable from the row payload alone (only
metacarries it), sodownstream consumers of
column.type— KPI numeric/tuple parsing(
src/core/kpi.ts), chart axis/role detection (src/core/chart-data.ts),variable-options validation (
src/core/variable-options.ts), compact typedisplay (
src/core/type-display.ts), and type-aware grid cell formatting —need a defined degraded behavior (e.g. treat as unknown/string type) for
servers in this gap, or those features need to be documented as
reduced-fidelity below the meta-line floor.
Acceptance
applyStreamLine()has a documented, tested meta-less fallback path(unit tests exercising a
row-before-metastream).column.typeconsumers have a defined, tested behavior whentype is unknown (not a silent
undefined/crash).tests/spike/clickhouse-client/) or adedicated regression test re-verifies the fix against a real pinned 24.8
image.
from the (Rejected)
@clickhouse/client-webadoption question.Relates to
docs/ADR-0005-clickhouse-web-client.md— where this wasdiscovered; the ADR's Rejected decision is scoped to
@clickhouse/client-webadoption and is independent of this bug.resolve (or be explicitly scoped around) before that matrix states a
ClickHouse-version floor.