Describe the bug
Building a pre-aggregation against a Microsoft Fabric data source fails immediately whenever any decimal/numeric column in the rollup's result set contains a NULL value.
During the build, Cube downloads the source query results via the Fabric driver, and the driver's decimal type handler calls .toString() on the value without a null-check, throwing:
TypeError: Cannot read properties of null (reading 'toString')
at Object.decimal (/cube/runtime/packages/cubejs-microsoftfabric-driver/src/MicrosoftFabricDriver.ts:66:14)
at /cube/runtime/packages/cubejs-microsoftfabric-driver/src/MicrosoftFabricDriver.ts:270:55
at Array.find (<anonymous>)
at /cube/runtime/packages/cubejs-microsoftfabric-driver/src/MicrosoftFabricDriver.ts:270:16
at Array.find (<anonymous>)
at /cube/runtime/packages/cubejs-microsoftfabric-driver/src/MicrosoftFabricDriver.ts:266:41
at Array.map (<anonymous>)
at MicrosoftFabricDriver.downloadQueryResults (/cube/runtime/packages/cubejs-microsoftfabric-driver/src/MicrosoftFabricDriver.ts:263:26)
at PreAggregationLoader.refreshReadOnlyExternalStrategy (/node_modules/@cubejs-backend/query-orchestrator/src/orchestrator/PreAggregationLoader.ts:734:19)
at Object.query (/node_modules/@cubejs-backend/query-orchestrator/src/orchestrator/QueryCache.ts:658:26)
The build runs under the read-only external strategy (refreshReadOnlyExternalStrategy) with no export bucket configured. NULLs are extremely common in real models (e.g. a LAG()-derived prior-period column is NULL for the earliest period; any ratio measure is NULL on a zero denominator), so this effectively blocks pre-aggregating those measures.
To Reproduce
Steps to reproduce the behavior:
- Configure a Cube deployment against a Microsoft Fabric data source.
- Define a cube with a numeric (decimal) measure that is
NULL for at least one row at the pre-aggregation's grain.
- Add a pre-aggregation that includes that measure plus a time dimension + granularity.
- Trigger the pre-aggregation build (e.g. from the Cube Cloud UI → Pre-Aggregations → Build).
- The build fails in ~1s with the
TypeError above.
Expected behavior
NULL values in decimal/numeric columns should be passed through as NULL — the type handler should null-check before calling .toString() — so pre-aggregations containing legitimately-null measures build successfully and preserve NULL semantics.
Screenshots
N/A — the Cube Cloud build-log error is included above.
Minimally reproducible Cube Schema
cube(`NullDecimalRepro`, {
// Two months; February's amount is NULL, so the monthly rollup
// materialises a NULL decimal -> driver crashes on build.
sql: `
SELECT CAST('2024-01-01' AS DATE) AS created_at, CAST(10.50 AS DECIMAL(10,2)) AS amount
UNION ALL
SELECT CAST('2024-02-01' AS DATE) AS created_at, CAST(NULL AS DECIMAL(10,2)) AS amount
`,
measures: {
maxAmount: {
sql: `amount`,
type: `max`,
},
},
dimensions: {
createdAt: {
sql: `created_at`,
type: `time`,
},
},
preAggregations: {
monthlyRollup: {
measures: [maxAmount],
timeDimension: createdAt,
granularity: `month`,
},
},
});
Version:
Cube Cloud — 1.6.57, cubejs-microsoftfabric-driver.
Additional context
- The crash is in the read-only external pre-aggregation strategy (
refreshReadOnlyExternalStrategy); the build had no export bucket configured.
CAST(... AS FLOAT) does not work around it — the column's type changes but the value is still NULL, and it's still routed through the crashing handler. The only schema-level workaround is COALESCE(..., <sentinel>), which is lossy: it destroys legitimate NULL semantics (e.g. it breaks WHEN col IS NULL THEN ... logic and forces a fake 0/sentinel for "no data" periods).
- Likely fix: null-check in the
decimal (and probably the other numeric) type handler(s) used by downloadQueryResults before calling .toString().
Describe the bug
Building a pre-aggregation against a Microsoft Fabric data source fails immediately whenever any decimal/numeric column in the rollup's result set contains a
NULLvalue.During the build, Cube downloads the source query results via the Fabric driver, and the driver's
decimaltype handler calls.toString()on the value without a null-check, throwing:The build runs under the read-only external strategy (
refreshReadOnlyExternalStrategy) with no export bucket configured. NULLs are extremely common in real models (e.g. aLAG()-derived prior-period column is NULL for the earliest period; any ratio measure is NULL on a zero denominator), so this effectively blocks pre-aggregating those measures.To Reproduce
Steps to reproduce the behavior:
NULLfor at least one row at the pre-aggregation's grain.TypeErrorabove.Expected behavior
NULLvalues in decimal/numeric columns should be passed through asNULL— the type handler should null-check before calling.toString()— so pre-aggregations containing legitimately-null measures build successfully and preserve NULL semantics.Screenshots
N/A — the Cube Cloud build-log error is included above.
Minimally reproducible Cube Schema
Version:
Cube Cloud —
1.6.57,cubejs-microsoftfabric-driver.Additional context
refreshReadOnlyExternalStrategy); the build had no export bucket configured.CAST(... AS FLOAT)does not work around it — the column's type changes but the value is still NULL, and it's still routed through the crashing handler. The only schema-level workaround isCOALESCE(..., <sentinel>), which is lossy: it destroys legitimate NULL semantics (e.g. it breaksWHEN col IS NULL THEN ...logic and forces a fake0/sentinel for "no data" periods).decimal(and probably the other numeric) type handler(s) used bydownloadQueryResultsbefore calling.toString().