pandas-dev/pandas · error · ValueError
Quarter must be 1 <= q <= 4
Error message
Quarter must be 1 <= q <= 4
What it means
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').
Source
Thrown at pandas/core/arrays/period.py:1598
day = 1
if quarter is not None:
if freq is None:
freq = to_offset("Q", is_period=True)
base = FreqGroup.FR_QTR.value
else:
freq = to_offset(freq, is_period=True)
base = libperiod.freq_to_dtype_code(freq)
if base != FreqGroup.FR_QTR.value:
raise AssertionError("base must equal FR_QTR")
freqstr = freq.freqstr
year, quarter = _make_field_arrays(year, quarter)
year = _field_to_int64(year)
quarter = _field_to_int64(quarter)
if (quarter < 1).any() or (quarter > 4).any():
raise ValueError("Quarter must be 1 <= q <= 4")
# Vectorized quarter_to_myear
mnum = MONTH_NUMBERS[parsing.get_rule_month(freqstr)] + 1
months = (mnum + (quarter - 1) * 3) % 12 + 1
years = np.where(months > mnum, year - 1, year)
length = len(years)
ones = np.ones(length, dtype=np.int64)
zeros = np.zeros(length, dtype=np.int64)
ordinals = libperiod.period_ordinals_from_fields(
years, months, ones, zeros, zeros, zeros, base
)
else:
freq = to_offset(freq, is_period=True)
base = libperiod.freq_to_dtype_code(freq)
arrays = _make_field_arrays(year, month, day, hour, minute, second)
ordinals = libperiod.period_ordinals_from_fields(
_field_to_int64(arrays[0]),View on GitHub (pinned to 71959b8cb9)
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.
Example fix
// before pd.period_range(year=2020, quarter=(month // 3), freq='Q') // after pd.period_range(year=2020, quarter=((month - 1) // 3) + 1, freq='Q')
Defensive patterns
Strategy: validation
Validate before calling
import pandas as pd
def period_range_quarter(year, quarter, freq='Q'):
q = pd.Series(quarter)
if ((q < 1) | (q > 4)).any():
raise ValueError(f'quarter out of range: {q.tolist()}')
return pd.period_range(year=year, quarter=quarter, freq=freq) Type guard
def valid_quarter(q) -> bool:
import numpy as np
arr = np.asarray(q)
return bool(((arr >= 1) & (arr <= 4)).all()) Try / catch
try:
rng = pd.period_range(year=year, quarter=quarter, freq='Q')
except ValueError as e:
if 'Quarter must be' in str(e):
quarter = [min(4, max(1, q)) for q in quarter]
rng = pd.period_range(year=year, quarter=quarter, freq='Q')
else:
raise Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- start and end must have same freq
- start and end must not be NaT
- Could not infer freq from start/end
- cannot convert float NaN to integer
- Mismatched Period array lengths
AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07).
Data as JSON: /api/errors/85b4fb6e62c8211a.
Report an issue: GitHub.