octobercms/october · error · SystemException
Invalid compare specifier
Error message
Invalid compare specifier
What it means
getRequestedCompareInterval() accepts only two compare specifiers — 'prev-period' and 'prev-year' — for building the comparison date range of a fetch request. A non-empty $compare that is neither throws 'Invalid compare specifier'. Empty/null skips comparison entirely, and a request with dateEnd but no dateStart silently skips it as well.
Source
Thrown at modules/dashboard/classes/ReportFetchData.php:281
return $date;
}
/**
* getRequestedCompareInterval
*/
protected function getRequestedCompareInterval(?string $compare): array
{
if (!$compare) {
return [null, null];
}
if (!$this->dateStart && $this->dateEnd) {
return [null, null];
}
if (!in_array($compare, ['prev-period', 'prev-year'])) {
throw new SystemException('Invalid compare specifier');
}
$periodCalculator = new ReportPeriodCalculator;
if ($compare === 'prev-period') {
$range = $periodCalculator->getPreviousPeriod($this->dateStart, $this->dateEnd);
}
else {
$range = $periodCalculator->getPreviousPeriodLastYear($this->dateStart, $this->dateEnd);
}
if (!$range) {
return [null, null];
}
return [$range->getStartDate(), $range->getEndDate()];
}
View on GitHub (pinned to b608633a7e)
Solutions
- Send exactly 'prev-period' or 'prev-year' (case-sensitive) as the compare value
- Omit the compare parameter entirely to disable period comparison
- Whitelist the compare value on the server before it reaches the fetch logic and reject unknown values with a 400 response
Example fix
// before $extraData['compare'] = 'previous'; // after $extraData['compare'] = 'prev-period'; // or omit the key to disable comparison unset($extraData['compare']);
Defensive patterns
Strategy: validation
Validate before calling
$compare = $extraData['compare'] ?? null;
if ($compare !== null && !in_array($compare, ['prev-period', 'prev-year'], true)) {
throw new InvalidArgumentException(
"Invalid compare specifier '{$compare}'. Use 'prev-period' or 'prev-year', or omit it."
);
} Type guard
function isValidCompareSpecifier(?string $compare): bool
{
return $compare === null
|| in_array($compare, ['prev-period', 'prev-year'], true);
} Try / catch
try {
$result = $widget->onFetchData($extraData);
} catch (SystemException $e) {
if (str_contains($e->getMessage(), 'Invalid compare specifier')) {
// bad client input: answer 400, not 500
return Response::make(['error' => $e->getMessage()], 400);
}
throw $e;
} Prevention
- Whitelist compare values at the request boundary and reject anything else with 400
- The value is case-sensitive: exactly 'prev-period' or 'prev-year'
- Omit the key entirely to disable comparison; note it is also skipped when only dateEnd is set
When it happens
Trigger: A data-fetch request whose extraData carries compare = 'previous', 'last_year', or 'prior-period' — any non-empty string other than exactly 'prev-period' or 'prev-year'.
Common situations: Custom AJAX handlers or third-party dashboard front-ends sending their own compare vocabulary; API clients guessing the parameter; values copied from another analytics tool's API.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- Current page is not set for a paginated query
- Unknown dimension type:
- Date dimensions cannot have fields.
- The dimension metric is already registered:
- Unknown dimension specified:
AI-assisted analysis of octobercms/october@b608633a7e (2026-08-21).
Data as JSON: /api/errors/a4aabf3c957168e5.
Report an issue: GitHub.