cube-js/cube · error
Unexpected stream end before row with types
Error message
Unexpected stream end before row with types
What it means
The ClickHouse driver uses the JSONCompactEachRowWithNamesAndTypes format, where the response stream must begin with two rows: one containing column names and the next containing column types. This error is thrown when the stream ends before the second (types) row arrives, meaning ClickHouse returned fewer rows than the format guarantees.
Source
Thrown at packages/cubejs-clickhouse-driver/src/ClickHouseDriver.ts:419
const allRowsIter = (async function* allRowsIter() {
for await (const rowsBatch of resultSetStream) {
for (const row of rowsBatch) {
yield row.json();
}
}
}());
const first = await allRowsIter.next();
if (first.done) {
throw new Error('Unexpected stream end before row with names');
}
// JSONCompactEachRowWithNamesAndTypes: expect first row to be column names as string
const names = first.value as Array<string>;
const second = await allRowsIter.next();
if (second.done) {
throw new Error('Unexpected stream end before row with types');
}
// JSONCompactEachRowWithNamesAndTypes: expect first row to be column names as string
const types = second.value as Array<string>;
if (names.length !== types.length) {
throw new Error(`Unexpected names and types length mismatch; names ${names.length} vs types ${types.length}`);
}
const dataRowsIter = (async function* () {
for await (const row of allRowsIter) {
yield transformStreamRow(row, names, types);
}
}());
const rowStream = Readable.from(dataRowsIter);
return {
rowStream,
types: names.map((name, idx) => {View on GitHub (pinned to 7d981676b3)
Solutions
- Check network stability between the driver and ClickHouse (proxies, LB idle/read timeouts) and raise streaming read timeouts
- Retry the query; transient truncation is often resolved on retry
- Verify the ClickHouse server version supports JSONCompactEachRowWithNamesAndTypes correctly
- Capture ClickHouse server logs for the query id at the time of failure to find server-side errors
- Upgrade the cubejs-clickhouse-driver package to pick up stream-robustness fixes
Example fix
// before
try {
await dataSourceStorage.downloadQueryResults(query, values);
} catch (e) { /* unhandled: Unexpected stream end before row with types */ }
// after
try {
return await driver.downloadQueryResults(query, values);
} catch (e) {
if (String(e.message).includes('Unexpected stream end')) {
return driver.downloadQueryResults(query, values); // retry transient truncation
}
throw e;
} Defensive patterns
Strategy: retry
Validate before calling
null
Type guard
null
Try / catch
try { await driver.downloadQueryResults(q, v); } catch (e) { if (/Unexpected stream end/.test(String(e))) { /* retry */ } else throw e; } Prevention
- Ensure stable network to ClickHouse
- Raise streaming timeouts
When it happens
Trigger: Calling downloadQueryResults() which invokes stream(); the allRowsIter yields the first row (names) but next() returns done before a second row — e.g. ClickHouse returns an empty or malformed result body, or the connection is cut off after the first row.
Common situations: Network interruptions between driver and ClickHouse; ClickHouse server errors that truncate the response; proxies/load balancers with aggressive timeouts cutting streaming responses; ClickHouse returning a partial body on large result sets; mismatched ClickHouse versions or format support.
Related errors
- Stream query failed: ${e}; query id: ${queryId}
- Connection check failed: ${errorMessage}
- Unexpected stream end before row with names
- Unexpected names and types length mismatch; names ${names.le
- Unexpected row and names/types length mismatch; row ${row.le
AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02).
Data as JSON: /api/errors/4291013c7ccaf7c4.
Report an issue: GitHub.