octobercms/october · error · SystemException

Cannot use both limit and pagination.

Error message

Cannot use both limit and pagination.

What it means

ReportQueryBuilder::validate() rejects having both $limit and $pagination set, throwing SystemException('Cannot use both limit and pagination.'). limit caps the row count (TOP N) while pagination implies paged result sets with page/pageSize semantics; combining them would make the resulting SQL ambiguous (limit inside or outside the page window). Set exactly one of the two before the builder validates.

Source

Thrown at modules/dashboard/classes/ReportQueryBuilder.php:677

    {
        if (!$this->tableName) {
            throw new SystemException('Table name is required.');
        }

        if (!$this->dimension) {
            throw new SystemException('Dimension is required.');
        }

        if (($this->dateStart || $this->dateEnd) && $this->startTimestamp !== null) {
            throw new SystemException('Cannot use both date range and timestamp filtering.');
        }

        if (!$this->dateStart && $this->startTimestamp === null) {
            throw new SystemException('Either date range or start timestamp is required.');
        }

        if ($this->limit !== null && $this->pagination !== null) {
            throw new SystemException('Cannot use both limit and pagination.');
        }
    }

    /**
     * buildQuery constructs the query builder
     *
     * @param bool $skipGrouping
     * @param bool $skipPagination
     * @param array $forceMetrics
     * @return QueryBuilder
     */
    protected function buildQuery(
        bool $skipGrouping = false,
        bool $skipPagination = false,
        array $forceMetrics = []
    ): QueryBuilder {
        $query = Db::table($this->tableName);

View on GitHub (pinned to b608633a7e)

Solutions

  1. Keep one mechanism: for a fixed Top N use setLimit($n) and remove pagination setup; for paged output use setPagination($page, $perPage) and remove the limit.
  2. When loading saved widget properties, unset 'limit' whenever pagination params are present (or vice versa) before configuring the builder.
  3. Sanitize request input: accept either per_page/page or limit, never merge both into the builder.
  4. Reset builder state if the same instance is reused for different widgets.

Example fix

// before
$builder->setLimit(10)->setPagination(2, 20);

// after (Top N)
$builder->setLimit(10);
// or (paged)
// $builder->setPagination(2, 20);
Defensive patterns

Strategy: validation

Validate before calling

$props = $widget->properties;
if (!empty($props['use_pagination'])) {
    unset($props['limit']);
    $builder->setPagination($page, $perPage);
} else {
    unset($props['page'], $props['per_page']);
    $builder->setLimit((int) ($props['limit'] ?? 10));
}

Prevention

When it happens

Trigger: Calling both $builder->setLimit(10) and $builder->setPagination(...) (or the fluent limit()/paginate() equivalents) on the same builder; a widget config merge that retains a saved 'limit' while the request adds pagination params (e.g. ?page=2&per_page=20).

Common situations: Upgrading a widget from fixed Top-N output to a paginated table while the stored widget properties still contain the old limit value; copy-pasting builder setup between widgets; front-end sending both pageSize and limit keys.

Related errors


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