octobercms/october · error · SystemException

The aggregate function is not supported:

Error message

The aggregate function is not supported: 

What it means

After the empty-string checks, the ReportMetric constructor validates $aggregateFunction against a fixed whitelist: sum, avg, min, max, count, none, count_distinct, count_distinct_not_null (the AGGREGATE_* constants). Any other non-empty string throws SystemException with the offending value appended. This is the enforcement point for the closed set of SQL aggregates the report query builder knows how to render; there is no extension mechanism here, so custom functions cannot be registered.

Source

Thrown at modules/dashboard/classes/ReportMetric.php:93

        }

        if (!strlen($aggregateFunction)) {
            throw new SystemException('The aggregate function cannot be empty.');
        }

        $knownAggregateFunctions = [
            self::AGGREGATE_SUM,
            self::AGGREGATE_AVG,
            self::AGGREGATE_MIN,
            self::AGGREGATE_MAX,
            self::AGGREGATE_COUNT,
            self::AGGREGATE_NONE,
            self::AGGREGATE_COUNT_DISTINCT,
            self::AGGREGATE_COUNT_DISTINCT_NOT_NULL
        ];

        if (!in_array($aggregateFunction, $knownAggregateFunctions)) {
            throw new SystemException('The aggregate function is not supported: ' . $aggregateFunction);
        }

        $this->code = $code;
        $this->databaseColumnName = $databaseColumnName;
        $this->displayName = $displayName;
        $this->aggregateFunction = $aggregateFunction;
        $this->intlFormatOptions = $intlFormatOptions;
    }

    /**
     * Returns the metric code.
     * @return string Returns the metric code.
     */
    public function getCode(): string
    {
        return $this->code;
    }

View on GitHub (pinned to b608633a7e)

Solutions

  1. Use the class constants: ReportMetric::AGGREGATE_SUM, AGGREGATE_AVG, AGGREGATE_MIN, AGGREGATE_MAX, AGGREGATE_COUNT, AGGREGATE_NONE, AGGREGATE_COUNT_DISTINCT, AGGREGATE_COUNT_DISTINCT_NOT_NULL.
  2. Normalize incoming config values: strtolower + str_replace(' ', '_', ...) mapped to the constants before constructing.
  3. If you need COUNT(DISTINCT col), use AGGREGATE_COUNT_DISTINCT — the column goes in the databaseColumnName argument, not the function string.
  4. If you truly need an unsupported aggregate, compute it in the data source query/view and expose the result with AGGREGATE_NONE.

Example fix

// before
new ReportMetric('customers', 'customer_id', 'Customers', 'distinct_count');

// after
new ReportMetric('customers', 'customer_id', 'Customers', ReportMetric::AGGREGATE_COUNT_DISTINCT);
Defensive patterns

Strategy: validation

Validate before calling

const AGGREGATES = [
    ReportMetric::AGGREGATE_SUM, ReportMetric::AGGREGATE_AVG,
    ReportMetric::AGGREGATE_MIN, ReportMetric::AGGREGATE_MAX,
    ReportMetric::AGGREGATE_COUNT, ReportMetric::AGGREGATE_NONE,
    ReportMetric::AGGREGATE_COUNT_DISTINCT, ReportMetric::AGGREGATE_COUNT_DISTINCT_NOT_NULL,
];

function normalizeAggregate(string $raw): string {
    $map = ['distinct_count' => 'count_distinct', 'countdistinct' => 'count_distinct'];
    $fn = $map[strtolower(trim($raw))] ?? strtolower(trim($raw));
    if (!in_array($fn, AGGREGATES, true)) {
        throw new InvalidArgumentException("Unsupported aggregate function: {$raw}");
    }
    return $fn;
}

Type guard

function isValidAggregate(string $fn): bool
{
    return in_array($fn, [
        ReportMetric::AGGREGATE_SUM, ReportMetric::AGGREGATE_AVG,
        ReportMetric::AGGREGATE_MIN, ReportMetric::AGGREGATE_MAX,
        ReportMetric::AGGREGATE_COUNT, ReportMetric::AGGREGATE_NONE,
        ReportMetric::AGGREGATE_COUNT_DISTINCT, ReportMetric::AGGREGATE_COUNT_DISTINCT_NOT_NULL,
    ], true);
}

Prevention

When it happens

Trigger: Passing a raw SQL-ish string like 'COUNT(DISTINCT x)', 'SUM', 'Average', 'distinct_count', or a typo like 'cont' as the 4th constructor argument. Case matters: the constants are lowercase, so 'SUM' fails in_array without strict comparison only by luck — stick to constants.

Common situations: Copy-pasting SQL into the metric definition; using a hypothetical constant name that does not exist; mapping a REST/UI payload field with different naming ('countDistinct' vs 'count_distinct') straight into the constructor.

Related errors


AI-assisted analysis of octobercms/october@b608633a7e (2026-08-21). Data as JSON: /api/errors/cefa348c73f75120. Report an issue: GitHub.