pandas-dev/pandas · error · ValueError

Of the three parameters: start, end, and periods, exactly…

Error message

Of the three parameters: start, end, and periods, exactly two must be specified

What it means

Raised by _get_ordinal_range (used by period_range and PeriodIndex construction) when count_not_none(start, end, periods) != 2. Exactly two of the three must be supplied so the range endpoints are unambiguous.

Solutions

  1. Pass exactly two of start, end, periods.
  2. Gate inputs: pick end+periods when start is None, start+end otherwise.
  3. Default missing values explicitly so exactly two are non-None at the call site.

Example fix

# before
pd.period_range(start='2020', end='2021', periods=12, freq='M')
# after
pd.period_range(start='2020', end='2021', freq='M')  # let pandas count
Defensive patterns

Strategy: validation

Validate before calling

from pandas.core.common import count_not_none

def exactly_two_of_three(start, end, periods) -> bool:
    return count_not_none(start, end, periods) == 2

Type guard

def valid_range_args(start, end, periods) -> bool:
    return sum(x is not None for x in (start, end, periods)) == 2

Try / catch

try:
    pr = pd.period_range(start=start, end=end, periods=periods, freq=freq)
except ValueError as e:
    if 'exactly two' in str(e):
        raise ValueError('Supply exactly two of start/end/periods') from e
    raise

Prevention

When it happens

Trigger: Calling pd.period_range(start=..., end=..., periods=...) with all three; with only one; or with none. Same family of misuse as date_range's equivalent guard.

Common situations: Dynamic code where some of start/end/periods come from optional user input and default to None; copy-paste from a date_range call that added a third argument; off-by-one in argument plumbing.

Related errors


AI-assisted analysis of pandas-dev/pandas@3b7651241d (2026-08-11). Data as JSON: /api/errors/35c99bd70bdf9a8b. Report an issue: GitHub.

Appendix: source

Thrown at pandas/core/arrays/period.py:1519

                    "Pass `freq` explicitly instead.",
                    Pandas4Warning,
                    stacklevel=find_stack_level(),
                )
                freq = inferred_freq
            data = data._values

    elif isinstance(data, (ABCIndex, ABCSeries)):
        data = data._values

    reso = get_unit_from_dtype(data.dtype)
    freq = Period._maybe_convert_freq(freq)
    base = freq._period_dtype_code
    return c_dt64arr_to_periodarr(data.view("i8"), base, tz, reso=reso), freq


def _get_ordinal_range(start, end, periods, freq, mult: int = 1):
    if com.count_not_none(start, end, periods) != 2:
        raise ValueError(
            "Of the three parameters: start, end, and periods, "
            "exactly two must be specified"
        )

    if freq is not None:
        freq = to_offset(freq, is_period=True)
        mult = freq.n

    if start is not None:
        start = Period(start, freq)
    if end is not None:
        end = Period(end, freq)

    is_start_per = isinstance(start, Period)
    is_end_per = isinstance(end, Period)

    if is_start_per and is_end_per and start.freq != end.freq:
        raise ValueError("start and end must have same freq")

View on GitHub (pinned to 3b7651241d)