cube-js/cube · error · UserError

Ungrouped query requires primary keys to be present in dimen

Error message

Ungrouped query requires primary keys to be present in dimensions: ${missingPrimaryKeys.map(k => `'${k}'`).join(', ')}. Pass allowUngroupedWithoutPrimaryKey option to disable this check.

What it means

In ungrouped (raw rows) queries Cube does not aggregate, so it requires that the primary key of every joined cube is present among the dimensions — otherwise rows cannot be uniquely identified and results may be wrong or duplicated. When required primary keys are missing and allowUngroupedWithoutPrimaryKey is not set, it throws this UserError.

Source

Thrown at packages/cubejs-schema-compiler/src/adapter/BaseQuery.js:622

    return R.fromPairs(
      this.measures.map(m => [m.unescapedAliasName(), m.measure]).concat(
        this.dimensions.map(m => [m.unescapedAliasName(), m.dimension])
      ).concat(
        this.timeDimensions.filter(m => !!m.granularity)
          .map(m => [m.unescapedAliasName(), `${m.dimension}.${m.granularity}`])
      )
    );
  }

  initUngrouped() {
    this.ungrouped = this.options.ungrouped;
    if (this.ungrouped) {
      if (!this.options.allowUngroupedWithoutPrimaryKey && this.join) {
        const cubes = R.uniq([this.join.root].concat(this.join.joins.map(j => j.originalTo)));
        const primaryKeyNames = cubes.flatMap(c => this.primaryKeyNames(c));
        const missingPrimaryKeys = primaryKeyNames.filter(key => !this.dimensions.find(d => d.dimension === key));
        if (missingPrimaryKeys.length) {
          throw new UserError(`Ungrouped query requires primary keys to be present in dimensions: ${missingPrimaryKeys.map(k => `'${k}'`).join(', ')}. Pass allowUngroupedWithoutPrimaryKey option to disable this check.`);
        }
      }
      if (this.measureFilters.length) {
        throw new UserError('Measure filters aren\'t allowed in ungrouped query');
      }
    }
  }

  get subQueryDimensions() {
    // eslint-disable-next-line no-underscore-dangle
    if (!this._subQueryDimensions) {
      // eslint-disable-next-line no-underscore-dangle
      this._subQueryDimensions = this.collectFromMembers(
        false,
        this.collectSubQueryDimensionsFor.bind(this),
        'collectSubQueryDimensionsFor'
      );
    }

View on GitHub (pinned to 7d981676b3)

Solutions

  1. Add each listed missing primary key (e.g. 'Orders.id') to the query dimensions
  2. Set allowUngroupedWithoutPrimaryKey: true in query options to bypass the check (accepting the risk)
  3. Remove unnecessary joins so fewer primary keys are required

Example fix

// before
dimensions: ['Orders.status'], ungrouped: true, joins: [Users]
// after
dimensions: ['Orders.status', 'Users.id'], ungrouped: true
Defensive patterns

Strategy: validation

Validate before calling

function ensureUngroupedPrimaryKeys(query, primaryKeyOf) {
  if (!query.ungrouped) return;
  const cubes = joinedCubesOf(query); // from joins + dimensions
  const missing = cubes
    .map(c => primaryKeyOf(c))
    .filter(pk => !query.dimensions.includes(pk));
  if (missing.length && !query.allowUngroupedWithoutPrimaryKey) {
    query.dimensions.push(...missing);
  }
}
ensureUngroupedPrimaryKeys(query, pkIndex);

Try / catch

try {
  await cubeApi.load(query);
} catch (e) {
  if (/Ungrouped query requires primary keys/.test(e.message)) {
    const missing = /dimensions: (.+)\./.exec(e.message)[1].split(', ');
    query.dimensions.push(...missing);
    return cubeApi.load(query);
  }
  throw e;
}

Prevention

When it happens

Trigger: Running a query with ungrouped mode (e.g. using the raw/ungrouped option or row-level queries) whose join involves cubes whose primary keys are not included in the dimensions list.

Common situations: Building record-detail/drill-down endpoints that select joined cube members but forget to include id dimensions; adding a join to an existing ungrouped query.

Related errors


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