{"record":{"id":"85b4fb6e62c8211a","repo":"pandas-dev/pandas","slug":"quarter-must-be-1-q-4","errorCode":null,"errorMessage":"Quarter must be 1 <= q <= 4","messagePattern":"Quarter must be 1 <= q <= 4","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"pandas/core/arrays/period.py","lineNumber":1598,"sourceCode":"        day = 1\n\n    if quarter is not None:\n        if freq is None:\n            freq = to_offset(\"Q\", is_period=True)\n            base = FreqGroup.FR_QTR.value\n        else:\n            freq = to_offset(freq, is_period=True)\n            base = libperiod.freq_to_dtype_code(freq)\n            if base != FreqGroup.FR_QTR.value:\n                raise AssertionError(\"base must equal FR_QTR\")\n\n        freqstr = freq.freqstr\n        year, quarter = _make_field_arrays(year, quarter)\n        year = _field_to_int64(year)\n        quarter = _field_to_int64(quarter)\n\n        if (quarter < 1).any() or (quarter > 4).any():\n            raise ValueError(\"Quarter must be 1 <= q <= 4\")\n\n        # Vectorized quarter_to_myear\n        mnum = MONTH_NUMBERS[parsing.get_rule_month(freqstr)] + 1\n        months = (mnum + (quarter - 1) * 3) % 12 + 1\n        years = np.where(months > mnum, year - 1, year)\n\n        length = len(years)\n        ones = np.ones(length, dtype=np.int64)\n        zeros = np.zeros(length, dtype=np.int64)\n        ordinals = libperiod.period_ordinals_from_fields(\n            years, months, ones, zeros, zeros, zeros, base\n        )\n    else:\n        freq = to_offset(freq, is_period=True)\n        base = libperiod.freq_to_dtype_code(freq)\n        arrays = _make_field_arrays(year, month, day, hour, minute, second)\n        ordinals = libperiod.period_ordinals_from_fields(\n            _field_to_int64(arrays[0]),","sourceCodeStart":1580,"sourceCodeEnd":1616,"githubUrl":"https://github.com/pandas-dev/pandas/blob/71959b8cb9b2459c16e14b34f28b178ccfe14735/pandas/core/arrays/period.py#L1580-L1616","documentation":"Raised in _range_from_fields when a vectorized `quarter` array contains values outside [1,4]. quarters are mapped to month ordinals arithmetically, so an out-of-range quarter would produce nonsense calendar dates; pandas rejects it up front. It is reached via period_range(year=..., quarter=..., freq='Q').","triggerScenarios":"pd.period_range(year=2020, quarter=5, freq='Q'); period_range(year=[2020,2021], quarter=[1,7]); quarter computed mod-1 or off-by-one from a 0-based month formula (quarter = month // 3 yielding 0 or 4).","commonSituations":"Deriving quarter from month with integer division that yields 0 (Jan) or 4 (Dec) instead of 1-4; UI dropdowns returning 0-indexed quarters; data entry or CSV with quarter=0 or quarter=5.","solutions":["Compute quarter as ((month - 1) // 3) + 1 to guarantee the 1-4 range.","Clip/validate the quarter column before calling: assert quarter between 1 and 4.","If passing a scalar quarter, ensure it is a literal in 1..4."],"exampleFix":"// before\npd.period_range(year=2020, quarter=(month // 3), freq='Q')\n// after\npd.period_range(year=2020, quarter=((month - 1) // 3) + 1, freq='Q')","handlingStrategy":"validation","validationCode":"import pandas as pd\n\ndef period_range_quarter(year, quarter, freq='Q'):\n    q = pd.Series(quarter)\n    if ((q < 1) | (q > 4)).any():\n        raise ValueError(f'quarter out of range: {q.tolist()}')\n    return pd.period_range(year=year, quarter=quarter, freq=freq)","typeGuard":"def valid_quarter(q) -> bool:\n    import numpy as np\n    arr = np.asarray(q)\n    return bool(((arr >= 1) & (arr <= 4)).all())","tryCatchPattern":"try:\n    rng = pd.period_range(year=year, quarter=quarter, freq='Q')\nexcept ValueError as e:\n    if 'Quarter must be' in str(e):\n        quarter = [min(4, max(1, q)) for q in quarter]\n        rng = pd.period_range(year=year, quarter=quarter, freq='Q')\n    else:\n        raise","preventionTips":["Derive quarter from month with ((month-1)//3)+1.","Validate quarter columns against [1,4] before period_range.","Treat 0-indexed quarters from UIs as a bug."],"tags":["pandas","period","period-range","quarter","bounds-check"],"analyzedSha":"71959b8cb9b2459c16e14b34f28b178ccfe14735","analyzedAt":"2026-08-07T01:30:20.476Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}