pandas-dev/pandas · error · ValueError

Neither `start` nor `end` can be NaT

Error message

Neither `start` nor `end` can be NaT

What it means

Raised by DatetimeArray._generate_range after coercing start/end to Timestamp when either is pd.NaT. Generating a range from a not-a-time boundary is impossible (the step math has no anchor), so it is rejected before unit/freq resolution proceeds.

Solutions

  1. Validate start/end before the call: if pd.isna(start) or pd.isna(end): handle missing-boundary case.
  2. Clean or filter NaT from the source column before passing its first/last value.
  3. Use try/except around the parse to substitute a default Timestamp when input is missing.

Example fix

// before
start = pd.to_datetime('')  # NaT
pd.date_range(start, periods=5, freq='D')  # ValueError: Neither start nor end can be NaT

// after
start = pd.to_datetime(user_input) if user_input else pd.Timestamp('2020-01-01')
pd.date_range(start, periods=5, freq='D')
Defensive patterns

Strategy: validation

Validate before calling

def safe_range_bound(bound):
    ts = pd.Timestamp(bound)
    if pd.isna(ts):
        raise ValueError(f"boundary {bound!r} parsed to NaT")
    return ts

Type guard

def is_valid_range_bound(bound) -> bool:
    return not pd.isna(pd.Timestamp(bound)) if bound is not None else False

Try / catch

try:
    rng = pd.date_range(start=start, periods=5, freq="D")
except ValueError as e:
    if "can be NaT" in str(e):
        start = pd.Timestamp.today().normalize()
        rng = pd.date_range(start=start, periods=5, freq="D")
    else:
        raise

Prevention

When it happens

Trigger: pd.date_range(start=pd.NaT, periods=5, freq='D'); pd.date_range(end=pd.NaT, ...); passing a column value that parsed to NaT (e.g. empty string, unparseable date) as start or end.

Common situations: Reading start/end from user input or config that may be blank; selecting index[0] from an index that contains NaT; chaining date_range to upstream parsing that can yield NaT.

Related errors


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

Appendix: source

Thrown at pandas/core/arrays/datetimes.py:422

        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"
            )
        freq = to_offset(freq)

        if start is not None:
            start = Timestamp(start)

        if end is not None:
            end = Timestamp(end)

        if start is NaT or end is NaT:
            raise ValueError("Neither `start` nor `end` can be NaT")

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

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

        left_inclusive, right_inclusive = validate_inclusive(inclusive)
        start, end = _maybe_normalize_endpoints(start, end, normalize)
        tz = _infer_tz_from_endpoints(start, end, tz)

        if tz is not None:
            # Localize the start and end arguments

View on GitHub (pinned to 3b7651241d)