{"record":{"id":"3193d7aa6d98fdf4","repo":"pandas-dev/pandas","slug":"must-specify-a-valid-frequency-freq","errorCode":null,"errorMessage":"Must specify a valid frequency: {freq}","messagePattern":"Must specify a valid frequency: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"pandas/core/arrays/arrow/array.py","lineNumber":4205,"sourceCode":"\n    def _dt_strftime(self, format: str) -> Self:\n        result = pc.strftime(self._pa_array, format=format)\n        return self._from_pyarrow_array(result)\n\n    def _round_temporally(\n        self,\n        method: Literal[\"ceil\", \"floor\", \"round\"],\n        freq,\n        ambiguous: TimeAmbiguous = \"raise\",\n        nonexistent: TimeNonexistent = \"raise\",\n    ) -> Self:\n        if ambiguous != \"raise\":\n            raise NotImplementedError(\"ambiguous is not supported.\")\n        if nonexistent != \"raise\":\n            raise NotImplementedError(\"nonexistent is not supported.\")\n        offset = to_offset(freq)\n        if offset is None:\n            raise ValueError(f\"Must specify a valid frequency: {freq}\")\n        pa_supported_unit = {\n            \"Y\": \"year\",\n            \"YS\": \"year\",\n            \"Q\": \"quarter\",\n            \"QS\": \"quarter\",\n            \"M\": \"month\",\n            \"MS\": \"month\",\n            \"W\": \"week\",\n            \"D\": \"day\",\n            \"h\": \"hour\",\n            \"min\": \"minute\",\n            \"s\": \"second\",\n            \"ms\": \"millisecond\",\n            \"us\": \"microsecond\",\n            \"ns\": \"nanosecond\",\n        }\n        unit = pa_supported_unit.get(offset._prefix, None)\n        if unit is None:","sourceCodeStart":4187,"sourceCodeEnd":4223,"githubUrl":"https://github.com/pandas-dev/pandas/blob/3b7651241d4da534b3559b60ef128e1c34f54116/pandas/core/arrays/arrow/array.py#L4187-L4223","documentation":"Raised by ArrowExtensionArray._round_temporally when `pandas.tseries.frequencies.to_offset(freq)` returns None, meaning the value cannot be parsed as a valid frequency/offset alias. The check happens before the supported-prefix lookup, so even valid-but-unsupported offsets never reach the later 'not supported' message.","triggerScenarios":"Calling `.dt.floor/.dt.ceil/.dt.round` on a timestamp[pyarrow] Series with a value that is not a frequency string or DateOffset, e.g. `ser.dt.floor('xyz')`, `ser.dt.floor(5)`, or `ser.dt.floor('')`.","commonSituations":"Typos in frequency aliases; passing a bare integer thinking it means 'N seconds'; building the freq dynamically from unvalidated input.","solutions":["Use a valid frequency alias such as 'h', 'D', 'min', 's', 'W', 'M', 'Q', or 'Y' (optionally with a multiplier like '2h').","If constructing freq programmatically, validate it with `pd.tseries.frequencies.to_offset(freq)` first and handle None."],"exampleFix":"# before\nser.dt.floor('xyz')\n# after\nser.dt.floor('h')","handlingStrategy":"validation","validationCode":"from pandas.tseries.frequencies import to_offset\nif to_offset(freq) is None:\n    raise ValueError(f\"invalid frequency: {freq!r}\")\nser.dt.floor(freq)","typeGuard":"def is_valid_freq(freq) -> bool:\n    from pandas.tseries.frequencies import to_offset\n    return to_offset(freq) is not None","tryCatchPattern":"try:\n    out = ser.dt.floor(freq)\nexcept ValueError as e:\n    if \"valid frequency\" in str(e):\n        out = ser.dt.floor('h')   # use a safe default\n    else:\n        raise","preventionTips":["Validate user-supplied freq with to_offset before rounding","Whitelist allowed frequency aliases at the input boundary"],"tags":["pyarrow","datetime-accessor","frequency","validation","arrow-extension"],"backgroundTag":null,"analyzedSha":"3b7651241d4da534b3559b60ef128e1c34f54116","analyzedAt":"2026-08-11T22:10:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}