flarum/framework · error · InvalidArgumentException

Relation aggregates can only be used with number attributes

Error message

Relation aggregates can only be used with number attributes

What it means

GetsRelationAggregates::relationAggregate() records an aggregate (count/sum/avg/min/max) over a relation to be computed for the attribute, but only when the attribute's type is a Number. Calling it on a non-numeric attribute throws this InvalidArgumentException at schema-definition time, because aggregates like SUM/AVG are meaningless on strings, dates-as-strings, JSON, etc.

Solutions

  1. Change the attribute's type to a Number type (or register the aggregate on a numeric attribute instead).
  2. Use countRelation only on attributes typed as Number; for non-numeric relations compute the aggregate manually in the serializer/model.
  3. If the column is conceptually numeric, fix the column cast/migration (e.g. integer cast) so the schema type resolves to Number.

Example fix

// before
$attribute->type('string')
    ->sumRelation('posts', 'views');
// after
$attribute->type(Number::class) // or use the numeric attribute definition
    ->sumRelation('posts', 'views');
Defensive patterns

Strategy: type-guard

Validate before calling

// before registering an aggregate, confirm the attribute type is numeric
if (! ($attribute->type ?? null) instanceof Number) {
    throw new \LogicException('Aggregate requires a Number-typed attribute');
}

Type guard

function attributeIsNumeric($attribute): bool {
    return $attribute instanceof SomeAttribute && $attribute->type instanceof Number;
}

Try / catch

try {
    $attribute->sumRelation('posts', 'views');
} catch (\InvalidArgumentException $e) {
    // fall back to manual aggregate computation in the serializer
}

Prevention

When it happens

Trigger: Calling countRelation/sumRelation/avgRelation/minRelation/maxRelation on an attribute whose $this->type is not an instance of Number — e.g. adding a sumRelation over a string column or a count aggregate on a boolean/date-typed attribute.

Common situations: Defining API serializers/attributes where the column was declared as string or date but developers assume counts are always allowed; copy-pasting an aggregate registration from a numeric field to a text field; a model column type changed from int to string without updating the schema extension.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of flarum/framework@4b939f6853 (2026-09-15). Data as JSON: /api/errors/ac6c4a764864c934. Report an issue: GitHub.

Appendix: source

Thrown at framework/core/src/Api/Schema/Concerns/GetsRelationAggregates.php:25

 * LICENSE file that was distributed with this source code.
 */

namespace Flarum\Api\Schema\Concerns;

use Closure;
use Tobyz\JsonApiServer\Schema\Type\Number;

trait GetsRelationAggregates
{
    /**
     * @var array{name: string, relation: string, column: string, function: string, constrain: Closure}|null
     */
    public ?array $relationAggregate = null;

    public function relationAggregate(string $relation, string $column, string $function, ?Closure $constrain = null): static
    {
        if (! $this->type instanceof Number) {
            throw new \InvalidArgumentException('Relation aggregates can only be used with number attributes');
        }

        $name = $this->name;

        $this->relationAggregate = compact('name', 'relation', 'column', 'function', 'constrain');

        return $this;
    }

    public function countRelation(string $relation, ?Closure $constrain = null): static
    {
        return $this->relationAggregate($relation, '*', 'count', $constrain);
    }

    public function sumRelation(string $relation, string $column, ?Closure $constrain = null): static
    {
        return $this->relationAggregate($relation, $column, 'sum', $constrain);
    }

View on GitHub (pinned to 4b939f6853)