hasura/graphql-engine · error · Error

Unexpected type of results.aggregates.count (${count}) expec

Error message

Unexpected type of results.aggregates.count (${count}) expecting number.

What it means

After running a count subquery for an exists expression, the engine reads results.aggregates.count and asserts it is a number. If the aggregate result is a string, object, or null-ish non-number, this internal invariant fails, indicating the subquery backend returned a malformed QueryResponse.

Source

Thrown at dc-agents/reference/src/query.ts:359

      aggregates: {
        count: { type: 'star_count' },
      },
      limit: 1, // We only need one row to exist to satisfy this expresion, this short circuits some filtering
      where:
        joinExpression !== undefined
          ? { type: 'and', expressions: [joinExpression, exists.where] } // Important: the join expression goes first to ensure short circuiting prevents unnecessary subqueries
          : exists.where,
    };

    const results = performSubquery(
      row,
      { type: 'table', table: targetTable },
      subquery,
    );
    const count = results.aggregates?.count ?? 0;

    if (typeof count != 'number') {
      throw new Error(
        `Unexpected type of results.aggregates.count (${count}) expecting number.`,
      );
    }

    return count > 0;
  };

const buildQueryForPathedOrderByElement = (
  orderByElement: OrderByElement,
  orderByRelations: Record<RelationshipName, OrderByRelation>,
): Query => {
  const [relationshipName, ...remainingPath] = orderByElement.target_path;
  if (relationshipName === undefined) {
    switch (orderByElement.target.type) {
      case 'column':
        const columnSelector = getColumnSelector(orderByElement.target.column);
        return {
          fields: {

View on GitHub (pinned to 724551b9ae)

Solutions

  1. Ensure the subquery layer returns aggregates.count as a JSON number, not a string or nested object.
  2. If wrapping the reference agent, coerce values at the driver boundary (Number(row.count)) before building the QueryResponse.
  3. Align @hasura/dc-api-types and agent versions so the QueryResponse contract matches.

Example fix

// before
return { rows: [], aggregates: { count: String(rowCount) } };

// after
return { rows: [], aggregates: { count: Number(rowCount) } };
Defensive patterns

Strategy: type-guard

Type guard

const isNumericCount = (r: QueryResponse): boolean =>
  r.aggregates === undefined || typeof r.aggregates.count === 'number';

Prevention

When it happens

Trigger: An exists expression is evaluated and performSubquery returns a QueryResponse whose aggregates.count is not a number — e.g. a backend returning { count: "5" }, { count: { value: 5 } }, or omitting aggregates in a non-conformant way combined with a truthy non-number value.

Common situations: Custom connector implementations of performSubquery that serialize counts as strings (common with SQLite/promise-mysql drivers), or version mismatches between @hasura/dc-api-types and the agent's query engine.

Related errors


AI-assisted analysis of hasura/graphql-engine@724551b9ae (2026-08-28). Data as JSON: /api/errors/0b1f2b948968467f. Report an issue: GitHub.