pandas-dev/pandas · error · ValueError

Of the four parameters: start, end, periods, and freq, exact

Error message

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

What it means

Raised by TimedeltaArray._generate_range when com.count_not_none(start, end, periods, freq) != 3. The range-generation contract is 'exactly three of the four must be specified' so the fourth is inferred; specifying fewer (under-determined) or all four (over-determined, contradictory) is ambiguous and rejected. This mirrors date_range's contract.

Source

Thrown at pandas/core/arrays/timedeltas.py:288

                unit = np.datetime_data(dtype)[0]

        data = sequence_to_td64ns(data, copy=copy, unit=unit)

        if dtype is not None:
            data = astype_overflowsafe(data, dtype=dtype, copy=False)

        return cls._simple_new(data, dtype=data.dtype)

    @classmethod
    def _generate_range(
        cls, start, end, periods, freq, closed=None, *, unit: TimeUnit
    ) -> Self:
        periods = dtl.validate_periods(periods)
        if freq is None and any(x is None for x in [periods, start, end]):
            raise ValueError("Must provide freq argument if no data is supplied")

        if com.count_not_none(start, end, periods, freq) != 3:
            raise ValueError(
                "Of the four parameters: start, end, periods, "
                "and freq, exactly three must be specified"
            )

        if start is not None:
            start = Timedelta(start).as_unit("ns")

        if end is not None:
            end = Timedelta(end).as_unit("ns")

        if unit not in ["s", "ms", "us", "ns"]:
            raise ValueError("'unit' must be one of 's', 'ms', 'us', 'ns'")

        if start is not None and unit is not None:
            start = start.as_unit(unit, round_ok=False)
        if end is not None and unit is not None:
            end = end.as_unit(unit, round_ok=False)

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Provide exactly three of start, end, periods, freq; drop or add one so the count is 3.
  2. If you want inferrable behavior, leave freq=None and pass start+end+periods.
  3. Audit call sites: ensure defaults do not collide (e.g. periods defaulting to a non-None value).

Example fix

# before
pd.timedelta_range(start='1 day', end='5 days', periods=5, freq='1D')  # 4 args
# after
pd.timedelta_range(start='1 day', end='5 days', periods=5)
Defensive patterns

Strategy: validation

Validate before calling

def exactly_three_of_four(start, end, periods, freq):
    count = sum(x is not None for x in (start, end, periods, freq))
    if count != 3:
        raise ValueError(f'Expected exactly 3 of 4, got {count}')
    return True

Type guard

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

Try / catch

try:
    return pd.timedelta_range(start, end, periods, freq)
except ValueError as e:
    if 'exactly three must be specified' in str(e):
        return pd.timedelta_range(start=start, end=end, periods=periods)
    raise

Prevention

When it happens

Trigger: Calling timedelta_range with only two of {start,end,periods,freq}, or with all four provided. Also when a caller supplies start+end+freq+periods simultaneously.

Common situations: Parameterized builders that conditionally set some args to None; copy-paste leaving an extra argument; refactoring from date_range without dropping the now-redundant arg.

Related errors


AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07). Data as JSON: /api/errors/ab3a28ca8297bb33. Report an issue: GitHub.