{"record":{"id":"a4aabf3c957168e5","repo":"octobercms/october","slug":"invalid-compare-specifier","errorCode":null,"errorMessage":"Invalid compare specifier","messagePattern":"Invalid compare specifier","errorType":"exception","errorClass":"SystemException","httpStatus":null,"severity":"error","filePath":"modules/dashboard/classes/ReportFetchData.php","lineNumber":281,"sourceCode":"\n        return $date;\n    }\n\n    /**\n     * getRequestedCompareInterval\n     */\n    protected function getRequestedCompareInterval(?string $compare): array\n    {\n        if (!$compare) {\n            return [null, null];\n        }\n\n        if (!$this->dateStart && $this->dateEnd) {\n            return [null, null];\n        }\n\n        if (!in_array($compare, ['prev-period', 'prev-year'])) {\n            throw new SystemException('Invalid compare specifier');\n        }\n\n        $periodCalculator = new ReportPeriodCalculator;\n\n        if ($compare === 'prev-period') {\n            $range = $periodCalculator->getPreviousPeriod($this->dateStart, $this->dateEnd);\n        }\n        else {\n            $range = $periodCalculator->getPreviousPeriodLastYear($this->dateStart, $this->dateEnd);\n        }\n\n        if (!$range) {\n            return [null, null];\n        }\n\n        return [$range->getStartDate(), $range->getEndDate()];\n    }\n","sourceCodeStart":263,"sourceCodeEnd":299,"githubUrl":"https://github.com/octobercms/october/blob/b608633a7e8922487d91a8161499020121c3b3bf/modules/dashboard/classes/ReportFetchData.php#L263-L299","documentation":"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.","triggerScenarios":"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'.","commonSituations":"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.","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"],"exampleFix":"// before\n$extraData['compare'] = 'previous';\n\n// after\n$extraData['compare'] = 'prev-period';\n// or omit the key to disable comparison\nunset($extraData['compare']);","handlingStrategy":"validation","validationCode":"$compare = $extraData['compare'] ?? null;\nif ($compare !== null && !in_array($compare, ['prev-period', 'prev-year'], true)) {\n    throw new InvalidArgumentException(\n        \"Invalid compare specifier '{$compare}'. Use 'prev-period' or 'prev-year', or omit it.\"\n    );\n}","typeGuard":"function isValidCompareSpecifier(?string $compare): bool\n{\n    return $compare === null\n        || in_array($compare, ['prev-period', 'prev-year'], true);\n}","tryCatchPattern":"try {\n    $result = $widget->onFetchData($extraData);\n} catch (SystemException $e) {\n    if (str_contains($e->getMessage(), 'Invalid compare specifier')) {\n        // bad client input: answer 400, not 500\n        return Response::make(['error' => $e->getMessage()], 400);\n    }\n    throw $e;\n}","preventionTips":["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"],"tags":["php","dashboard","report","fetch","compare","request-parameter"],"backgroundTag":"invalid-enum-value","analyzedSha":"b608633a7e8922487d91a8161499020121c3b3bf","analyzedAt":"2026-08-21T04:24:57.515Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}