{"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":1601,"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 FreqGroup.from_period_dtype_code(base) != FreqGroup.FR_QTR:\n                raise ValueError(\"freq must be a quarterly frequency\")\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":1583,"sourceCodeEnd":1619,"githubUrl":"https://github.com/pandas-dev/pandas/blob/3b7651241d4da534b3559b60ef128e1c34f54116/pandas/core/arrays/period.py#L1583-L1619","documentation":"Raised by _range_from_fields after _field_to_int64 conversion when any element of the quarter array is outside [1, 4]. Quarters are a closed 1..4 enumeration; anything else indicates bad input.","triggerScenarios":"pd.PeriodIndex(year=..., quarter=[0,1,2], freq='Q'); quarter=[1,2,5]; quarter derived from a 0-based index without adding 1; NaN-free float values like 4.5.","commonSituations":"Off-by-one when computing quarter from month ((month-1)//3 instead of (month-1)//3 + 1); 0-indexed user input; data-entry typo producing quarter=5.","solutions":["Compute quarter as (month - 1) // 3 + 1.","Clip or filter invalid quarters before passing: q = np.where((q>=1)&(q<=4), q, np.nan) then drop NaN.","Validate explicitly with assert df['quarter'].between(1,4).all()."],"exampleFix":"# before\nquarter = (month - 1) // 3  # 0..3\npd.PeriodIndex(year=year, quarter=quarter, freq='Q')\n# after\nquarter = (month - 1) // 3 + 1  # 1..4\npd.PeriodIndex(year=year, quarter=quarter, freq='Q')","handlingStrategy":"validation","validationCode":"import numpy as np\n\ndef quarters_in_range(quarter) -> bool:\n    q = np.asarray(quarter)\n    return bool(((q >= 1) & (q <= 4)).all())","typeGuard":"def is_valid_quarter(q) -> bool:\n    try:\n        return 1 <= int(q) <= 4\n    except (TypeError, ValueError):\n        return False","tryCatchPattern":"try:\n    pi = pd.PeriodIndex(year=y, quarter=q, freq='Q')\nexcept ValueError as e:\n    if 'Quarter must be' in str(e):\n        q = np.clip(np.asarray(q).astype(int), 1, 4)\n        pi = pd.PeriodIndex(year=y, quarter=q, freq='Q')\n    else:\n        raise","preventionTips":["Compute quarter as (month - 1) // 3 + 1, never a 0-based index.","Add df['quarter'].between(1,4).all() assertions in your pipeline.","Reject quarter == 0 or quarter == 5 in upstream validators."],"tags":["pandas","period","quarter","validation"],"backgroundTag":null,"analyzedSha":"3b7651241d4da534b3559b60ef128e1c34f54116","analyzedAt":"2026-08-11T22:10:44.015Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}