octobercms/october · error · SystemException
Either date range or start timestamp is required.
Error message
Either date range or start timestamp is required.
What it means
ReportQueryBuilder::validate() requires at least one time-filtering mode: either a date range (dateStart set) or a startTimestamp. When neither is present it throws SystemException('Either date range or start timestamp is required.'). The dashboard report queries are intentionally bounded in time (reporting over an unbounded table would scan everything), so the builder refuses to build an unrestricted aggregate query. Note the pairing guard: dateEnd alone is not sufficient — the check keys on dateStart.
Source
Thrown at modules/dashboard/classes/ReportQueryBuilder.php:673
*
* @throws SystemException
*/
protected function validate(): void
{
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,View on GitHub (pinned to b608633a7e)
Solutions
- Set a bounded window before building: $builder->setDateRange($start, $end) with a non-null start, or $builder->setStartTimestamp($ts).
- If you only have an end date, compute a start (e.g. $end->copy()->subDays(30)) so dateStart is set.
- Apply a sane default period in the widget/controller when the request carries no range, e.g. last 30 days.
- If you genuinely want an unbounded report, pass the earliest possible start timestamp for the table.
Example fix
// before
$builder->table('orders')->setDimension($dim)->applyMetrics($metrics);
$rows = $builder->buildQuery()->get();
// after
$builder->table('orders')->setDimension($dim)->applyMetrics($metrics)
->setStartTimestamp(strtotime('-30 days'));
$rows = $builder->buildQuery()->get(); Defensive patterns
Strategy: validation
Validate before calling
// Guarantee a bounded window before building
$start = $request->input('date_start') ?? now()->subDays(30)->toDateString();
$builder->setDateRange($start, $request->input('date_end'));
// note: dateStart must be set — dateEnd alone is not sufficient Prevention
- Always apply a default reporting period (e.g. last 30 days) in the widget/controller layer.
- Remember dateEnd alone does not satisfy the guard — dateStart or startTimestamp must be present.
- For 'all time' reports, pass the earliest business-plausible start timestamp.
When it happens
Trigger: Building a report query with only table + dimension + metrics, never calling setDateRange()/setDateStart()/setStartTimestamp(); passing dateEnd but not dateStart; a widget whose default period resolution returns null for both values (e.g. empty 'range' param from the UI).
Common situations: New widget that forgets to forward the time range; UI sends 'range=all' or an empty value which the mapping code turns into no filter at all; data source default period not applied because of a config key rename.
Related errors
- Cannot use both date range and timestamp filtering.
- Table name is required.
- Dimension is required.
- Cannot use both limit and pagination.
- Invalid aggregate function:
AI-assisted analysis of octobercms/october@b608633a7e (2026-08-21).
Data as JSON: /api/errors/1f169fad606fc715.
Report an issue: GitHub.