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

  1. Send exactly 'prev-period' or 'prev-year' (case-sensitive) as the compare value
  2. Omit the compare parameter entirely to disable period comparison
  3. 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

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


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