cube-js/cube · error · UserError

Pre-aggregation '${preAggregationName}' is already defined

Error message

Pre-aggregation '${preAggregationName}' is already defined

What it means

While inserting a new pre-aggregation into a cube's AST preAggregations object, convertJS checks existing property keys; if a property with the same identifier as preAggregationName already exists, it throws UserError 'Pre-aggregation 'name' is already defined' to prevent silent overwrites and duplicate definitions.

Source

Thrown at packages/cubejs-schema-compiler/src/compiler/converters/CubePreAggregationConverter.ts:59

        preAggregationNode = statement.expression;
      }
    }

    if (preAggregationNode === null) {
      throw new Error('Pre-aggregation definition is malformed');
    }

    let anchor: t.ObjectExpression | null = null;

    cubeDefinition.properties.forEach((prop) => {
      if (t.isObjectProperty(prop) && t.isIdentifier(prop.key)) {
        if (prop.key.name === 'preAggregations' && t.isObjectExpression(prop.value)) {
          anchor = prop.value;

          prop.value.properties.forEach((p) => {
            if (t.isObjectProperty(p) && t.isIdentifier(p.key)) {
              if (p.key.name === preAggregationName) {
                throw new UserError(`Pre-aggregation '${preAggregationName}' is already defined`);
              }
            }
          });
        }
      }
    });

    if (anchor === null) {
      cubeDefinition.properties.push(
        t.objectProperty(
          t.identifier('preAggregations'),
          t.objectExpression([t.objectProperty(t.identifier(preAggregationName), preAggregationNode)])
        )
      );
    } else {
      (<t.ObjectExpression>anchor).properties.push(
        t.objectProperty(t.identifier(preAggregationName), <t.ObjectExpression>preAggregationNode)
      );

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Rename the new pre-aggregation to a unique identifier
  2. Remove or merge the existing pre-aggregation with the same name
  3. Make generation code idempotent (skip names already present)
  4. Check for case differences that still resolve to the same identifier

Example fix

// before
preAggregations: {
  main: { ... },
  main: { ... } // duplicate
}

// after
preAggregations: {
  main: { ... },
  mainDaily: { ... }
}
Defensive patterns

Strategy: validation

Validate before calling

function assertUniquePreAggName(cubeDefinition, preAggregationName) {
  const keys = (cubeDefinition.preAggregations && Object.keys(cubeDefinition.preAggregations)) || [];
  if (keys.includes(preAggregationName)) {
    throw new Error(`Pre-aggregation '${preAggregationName}' would duplicate an existing definition`);
  }
}

Try / catch

try {
  converter.convert(astByCubeName);
} catch (e) {
  const m = String(e.message).match(/Pre-aggregation '(.+)' is already defined/);
  if (m) console.error(`Rename or merge duplicate rollup: ${m[1]}`);
}

Prevention

When it happens

Trigger: Adding or regenerating a pre-aggregation whose name collides with an existing key in the cube's preAggregations object — e.g. creating a rollup named 'main' when 'main' already exists, or re-running an auto-rollup generator against modified schemas.

Common situations: Query-based auto-rollups named after queries colliding with hand-written rollups, duplicated names after copy/pasting definitions between cubes, or re-running schema tooling twice without deduplication.

Related errors


AI-assisted analysis of cube-js/cube@7d981676b3 (2026-09-02). Data as JSON: /api/errors/1714367e67c83656. Report an issue: GitHub.