{"record":{"id":"468e7f099b0848ff","repo":"cube-js/cube","slug":"lambda-union-with-source-data-feature-is-supported","errorCode":null,"errorMessage":"Lambda union with source data feature is supported only by external rollups stored in Cube Store but was invoked for '${preAggregation.preAggregationId}'","messagePattern":"Lambda union with source data feature is supported only by external rollups stored in Cube Store but was invoked for '(.+?)'","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cubejs-query-orchestrator/src/orchestrator/PreAggregationLoadCache.ts","lineNumber":130,"sourceCode":"  protected async getTablesQuery(preAggregation) {\n    const redisKey = this.tablesCachePrefixKey(preAggregation);\n    if (!this.tables[redisKey]) {\n      const tables = this.preAggregations.options.skipExternalCacheAndQueue && preAggregation.external ?\n        await this.fetchTablesNoCache(preAggregation) :\n        await this.tablesFromCache(preAggregation);\n      if (tables === undefined) {\n        throw new Error('Pre-aggregation tables are undefined.');\n      }\n      this.tables[redisKey] = tables;\n    }\n    return this.tables[redisKey];\n  }\n\n  public async getTableColumnTypes(preAggregation: PreAggregationDescription, tableName: string): Promise<TableStructure> {\n    const prefixKey = this.tablesCachePrefixKey(preAggregation);\n    if (!this.tableColumnTypes[prefixKey]?.[tableName]) {\n      if (!this.preAggregations.options.skipExternalCacheAndQueue && preAggregation.external) {\n        throw new Error(`Lambda union with source data feature is supported only by external rollups stored in Cube Store but was invoked for '${preAggregation.preAggregationId}'`);\n      }\n      const client = await this.externalDriverFactory();\n      const columnTypes = await client.tableColumnTypes(tableName);\n      if (!this.tableColumnTypes[prefixKey]) {\n        this.tableColumnTypes[prefixKey] = {};\n      }\n      this.tableColumnTypes[prefixKey][tableName] = columnTypes;\n    }\n    return this.tableColumnTypes[prefixKey][tableName];\n  }\n\n  private async calculateVersionEntries(preAggregation): Promise<VersionEntriesObj> {\n    let versionEntries = tablesToVersionEntries(\n      preAggregation.preAggregationsSchema,\n      await this.getTablesQuery(preAggregation)\n    );\n    // It presumes strong consistency guarantees for external pre-aggregation tables ingestion\n    if (!preAggregation.external) {","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/cube-js/cube/blob/7d981676b36392fec34088b9afab6bdcad40207c/packages/cubejs-query-orchestrator/src/orchestrator/PreAggregationLoadCache.ts#L112-L148","documentation":"Cube's lambda (multi-stage) pre-aggregations that union over source data require the resulting rollup to be stored in Cube Store (an external store). getTableColumnTypes throws this when it is asked for column types of a table marked external=true while the skipExternalCacheAndQueue option is set, meaning the code path assumed a non-Cube-Store external rollup. It protects an internal invariant: source-data lambda unions are only valid for external rollups physically stored in Cube Store.","triggerScenarios":"Calling getTableColumnTypes for a preAggregation with external=true when this.preAggregations.options.skipExternalCacheAndQueue is truthy; configuring a lambda union pre-aggregation that rolls up to a non-Cube-Store external store while external caching/queue is skipped.","commonSituations":"Running a query whose pre-aggregation chain uses source data with a custom external driver configuration; test setups or options that set skipExternalCacheAndQueue; misconfigured rollups where externalFrom/external store does not point at Cube Store.","solutions":["Store the rollup in Cube Store: make sure the lambda union pre-aggregation is built as an external rollup (external: true in the rollup config and a Cube Store external store configured).","Remove or unset the skipExternalCacheAndQueue option so external cache and queue are used.","Restructure the pre-aggregation so lambda union source data is not required, or build the base pre-aggregation first (pre-aggregation chaining) instead of reading source data.","Check the rollup definition: source data lambda unions require a partitioned base pre-aggregation; define it and reference it instead of raw source data."],"exampleFix":"// before\noptions: { skipExternalCacheAndQueue: true }\n// after\noptions: { skipExternalCacheAndQueue: false } // rollup stored in Cube Store","handlingStrategy":"validation","validationCode":"const opts = preAggregationsOptions || {};\nif (rollupConfig.rollup_lambda && rollupConfig.external !== false && opts.skipExternalCacheAndQueue) {\n  throw new Error('Lambda union with source data requires Cube Store external rollups; unset skipExternalCacheAndQueue');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await loadCache.getTableColumnTypes(preAggregation, tableName);\n} catch (e) {\n  if (String(e.message).includes('Lambda union with source data')) {\n    // fall back to building in Cube Store or restructure rollup\n  }\n  throw e;\n}","preventionTips":["Always store lambda union rollups in Cube Store (external: true).","Never enable skipExternalCacheAndQueue for lambda unions with source data.","Define a base pre-aggregation for the lambda union instead of reading raw source rows.","Document external store requirements in your Cube configuration review checklist."],"tags":["pre-aggregation","lambda-union","cube-store","configuration"],"backgroundTag":"lambda-union-external-rollup-required","analyzedSha":"7d981676b36392fec34088b9afab6bdcad40207c","analyzedAt":"2026-09-02T03:45:10.400Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}