cube-js/cube · error · UserError

Pre-aggregation YAML must be a map/object

Error message

Pre-aggregation YAML must be a map/object

What it means

convertYaml parses a pre-aggregation YAML document with the yaml library and requires the root node to be a map. If the document is empty, a scalar, or a sequence, it throws UserError 'Pre-aggregation YAML must be a map/object' because pre-aggregation definitions must be key/value maps keyed by rollup name.

Source

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

          t.objectExpression([t.objectProperty(t.identifier(preAggregationName), preAggregationNode)])
        )
      );
    } else {
      (<t.ObjectExpression>anchor).properties.push(
        t.objectProperty(t.identifier(preAggregationName), <t.ObjectExpression>preAggregationNode)
      );
    }
  }

  protected convertYaml(cubeDefSet: YamlSet) {
    const { preAggregationName, code } = this.preAggregationDefinition;
    const { cubeDefinition } = cubeDefSet;

    const preAggDoc = YAML.parseDocument(code);
    const preAggNode = preAggDoc.contents;

    if (!preAggNode || !isMap(preAggNode)) {
      throw new UserError('Pre-aggregation YAML must be a map/object');
    }

    const preAggsPair = cubeDefinition.items.find(
      (pair: Pair) => isScalar(pair.key) && (pair.key.value === 'pre_aggregations' || pair.key.value === 'preAggregations')
    );

    if (preAggsPair) {
      const seq = preAggsPair.value;
      if (!YAML.isSeq(seq)) {
        throw new UserError('\'pre_aggregations\' must be a sequence');
      }

      const exists = seq.items.some(item => {
        if (isMap(item)) {
          const namePair = item.items.find(
            (pair: Pair) => isScalar(pair.key) && pair.key.value === 'name'
          );
          return namePair && isScalar(namePair.value) && namePair.value.value === preAggregationName;

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Make the YAML root a mapping: pre_aggregations: - name: ... structure (or a map of pre-aggregation names to definitions)
  2. Check the file is not empty or comment-only
  3. Wrap list content under a top-level key instead of a root-level sequence
  4. Validate the YAML parses to an object: YAML.parseDocument(code).contents is a YAMLMap

Example fix

# before
- measures:
    - orders.count

# after
pre_aggregations:
  - name: main
    measures:
      - orders.count
Defensive patterns

Strategy: validation

Validate before calling

const YAML = require('yaml');
function assertYamlIsMap(code) {
  const doc = YAML.parseDocument(code);
  if (!doc.contents || !YAML.isMap(doc.contents)) {
    throw new Error('Pre-aggregation YAML must be a root-level map/object');
  }
}

Try / catch

try {
  converter.convert(astByCubeName);
} catch (e) {
  if (e.message === 'Pre-aggregation YAML must be a map/object') {
    console.error('Check the pre-aggregation YAML file: root must be a mapping, not a list or empty doc');
  }
}

Prevention

When it happens

Trigger: Passing YAML that is an empty document, a bare list of measures, or a scalar string as the pre-aggregation code to CubePreAggregationConverter when the cube definition came from YAML.

Common situations: Hand-editing YAML and deleting the top-level mapping, generating YAML from tools that emit sequences, or a file that is empty/only comments so the parsed root is null.

Related errors


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