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
- Change the attribute's type to a Number type (or register the aggregate on a numeric attribute instead).
- Use countRelation only on attributes typed as Number; for non-numeric relations compute the aggregate manually in the serializer/model.
- 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
- Only call count/sum/avg/min/maxRelation on integer/decimal-typed attributes.
- Check the column's migration cast before wiring aggregates.
- Compute non-numeric aggregates manually instead of via relationAggregate.
- Keep column types in sync when refactoring model fields.
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)