mongodb/laravel-mongodb · error · InvalidArgumentException

Columns cannot be specified to create an aggregation…

Error message

Columns cannot be specified to create an aggregation builder. Add a $project stage instead.

What it means

When creating a fluent AggregationBuilder via ->aggregate() with no function, column selection is meaningless because projections must be expressed as a $project stage. Passing any columns other than ['*'] raises this InvalidArgumentException.

Solutions

  1. Call ->aggregate(null) (columns default to ['*']) and add a ->project(...) stage on the AggregationBuilder instead.
  2. If you want aggregation over specific columns, pass a function: ->aggregate('avg', ['price']).

Example fix

// before
$agg = Model::aggregate(null, ['price']);
// after
$agg = Model::aggregate(null)->project('price');
Defensive patterns

Strategy: validation

Validate before calling

if ($function === null && $columns !== ['*']) {
    $columns = ['*']; // or move columns into a $project stage
}
$agg = $model->aggregate($function, $columns);

Try / catch

try {
    $agg = Model::aggregate(null, $columns);
} catch (InvalidArgumentException $e) {
    if (str_contains($e->getMessage(), 'Columns cannot be specified')) {
        $agg = Model::aggregate(null)->project($columns);
    } else { throw $e; }
}

Prevention

When it happens

Trigger: Calling ->aggregate('avg', ['price']) — no wait, the builder path only triggers with $function === null; specifically ->aggregate(null, ['price']) or ->aggregate(null, 'price') cast to array.

Common situations: Developers mixing the fluent-builder call style (no function) with the aggregate-with-columns style, expecting column selection to carry over.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of mongodb/laravel-mongodb@0634653039 (2026-09-15). Data as JSON: /api/errors/86eb2c8d00baed3a. Report an issue: GitHub.

Appendix: source

Thrown at src/Query/Builder.php:582

        ];

        return md5(serialize(array_values($key)));
    }

    /** @return ($function is null ? AggregationBuilder : mixed) */
    #[Override]
    public function aggregate($function = null, $columns = ['*'])
    {
        assert(is_array($columns), new TypeError(sprintf('Argument #2 ($columns) must be of type array, %s given', get_debug_type($columns))));

        if ($function === null) {
            if (! trait_exists(FluentFactoryTrait::class)) {
                // This error will be unreachable when the mongodb/builder package will be merged into mongodb/mongodb
                throw new BadMethodCallException('Aggregation builder requires package mongodb/builder 0.2+');
            }

            if ($columns !== ['*']) {
                throw new InvalidArgumentException('Columns cannot be specified to create an aggregation builder. Add a $project stage instead.');
            }

            if ($this->wheres) {
                throw new BadMethodCallException('Aggregation builder does not support previous query-builder instructions. Use a $match stage instead.');
            }

            return new AggregationBuilder($this->collection, $this->options);
        }

        $this->aggregate = [
            'function' => $function,
            'columns' => $columns,
        ];

        $previousColumns = $this->columns;

        // We will also back up the select bindings since the select clause will be
        // removed when performing the aggregate function. Once the query is run

View on GitHub (pinned to 0634653039)