Skip to content

Microsoft Fabric driver: pre-aggregation build crashes on NULL decimal values (Cannot read properties of null (reading 'toString')) #11094

Description

@jdart

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:

  1. Configure a Cube deployment against a Microsoft Fabric data source.
  2. Define a cube with a numeric (decimal) measure that is NULL for at least one row at the pre-aggregation's grain.
  3. Add a pre-aggregation that includes that measure plus a time dimension + granularity.
  4. Trigger the pre-aggregation build (e.g. from the Cube Cloud UI → Pre-Aggregations → Build).
  5. 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().

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions