fix(cubejs-client-core): surface cubeSql error chunks instead of dropping rows#11097
fix(cubejs-client-core): surface cubeSql error chunks instead of dropping rows#11097keydunov wants to merge 2 commits into
Conversation
…ping rows
The cubesql endpoint streams JSONL (schema line, then data lines), and on a
post-processing failure appends a trailing `{ error }` chunk. The non-streaming
`cubeSql()` parser mapped every non-schema line through `JSON.parse(d).data`,
so the error line became an `undefined` phantom row and the failure was
silently swallowed — callers saw a "successful" result with a null row.
Classify each line and throw on an `error` chunk, mirroring how
`cubeSqlStream()` already handles it. Genuine parse failures still fall back to
the raw response body.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Consumer-side fixes (ai-engineer inline parsers + console-ui table hardening) live in cubedevinc/cubejs-enterprise#12668. |
|
Claude finished @keydunov's task in 43s —— View job Code ReviewThe fix correctly addresses the silent error-swallowing bug. The new Review details — looks good, a few small observationsWhat's good
Minor observations (non-blocking)
Security / PerformanceNothing to flag. The change is parse-logic only; no new I/O, no surface area for injection, and the iteration cost is identical to the prior |
Drop the `no-continue` violation by inverting the loop guard, and switch the regression test's error strings to single quotes per the lint rule. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Claude finished @keydunov's task in 29s —— View job Code ReviewThe latest commit ( Review detailsVerdictLGTM — no new issues. 0 high / 0 medium / 0 low. Re-reading the diff
Code quality / security / perfNothing to flag. Parse-only change, no new I/O or untrusted-input expansion. Iteration cost matches the prior TestingCoverage is adequate for the bug class. A nice-to-have (non-blocking) would be a test for an error chunk appearing between data chunks (data, then error, then more data) to lock in fail-loud-and-discard-partial semantics, but the current test exercises the reported real-world shape. Prior threadAlready reviewed and approved by @vasilev-alex. |
Problem
The cubesql endpoint streams JSONL — a schema line, then data lines — and on a post-processing failure appends a trailing
{ "error": "...", "requestId": "..." }chunk. The non-streamingcubeSql()parser mapped every non-schema line throughJSON.parse(d).data:For an
errorchunk,JSON.parse(d).dataisundefined, so it gets concatenated as a phantomundefinedrow and the failure is silently swallowed — callers receive a "successful" result with a null row instead of an error. Downstream this surfaced as e.g. a workbook table crashing on the null row, with the actual cast-error message lost.cubeSqlStream()already classifieserrorchunks correctly (it yields{ type: 'error' }); only the non-streamingcubeSql()was affected.Fix
Classify each JSONL line and throw on an
errorchunk, mirroringcubeSqlStream(). Genuine parse failures (malformed schema/data line) still fall back to throwing the raw response body, preserving existing behavior.Testing
Added a regression test to
CubeApi.test.tsusing the exact real-world payload (schema line followed by a post-processing cast-error chunk) assertingcubeSql()rejects with the clean message rather than resolving with a phantom row. AllcubeSqltests pass.🤖 Generated with Claude Code