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 in _generate_range after Timestamp coercion when either start or end is pandas.NaT. Generating a range anchored on NaT is meaningless — there is no real reference instant — so it is rejected up front, before the freq/periods arithmetic would propagate the NaT silently.
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 argumentsView on GitHub (pinned to 71959b8cb9)
Solutions
- Filter or fill NaT before using it as a bound: start = df['start'].dropna().iloc[0].
- Validate with pd.isna(start) / pd.isna(end) and branch.
- If the column may be empty, default to a real Timestamp or skip the range generation.
Example fix
# before
start = pd.to_datetime(maybe_bad_string) # may be NaT
pd.date_range(start, '2020-12-31', freq='D')
# after
start = pd.to_datetime(maybe_bad_string)
if pd.isna(start):
raise ValueError(f'unparseable start: {maybe_bad_string!r}')
pd.date_range(start, '2020-12-31', freq='D') Defensive patterns
Strategy: validation
Validate before calling
if pd.isna(start) or pd.isna(end):
raise ValueError('start/end cannot be NaT; supply a valid Timestamp') Type guard
def is_valid_bound(x) -> bool:
return x is None or (isinstance(x, (pd.Timestamp, str)) and not pd.isna(pd.Timestamp(x))) Try / catch
try:
pd.date_range(start, end, freq='D')
except ValueError as e:
if 'can be NaT' in str(e):
start = df['start'].dropna().iloc[0]
pd.date_range(start, end, freq='D')
else: raise Prevention
- dropna() the source column before taking the bound.
- Validate parsed timestamps with pd.isna before use.
When it happens
Trigger: pd.date_range(start=pd.NaT, end='2020-01-05', periods=3); pd.date_range(start='2020-01-01', end=pd.NaT, freq='D'); passing a column value that is NaT as the start/end (e.g. df['start'].iloc[0] when the column is all-NaT).
Common situations: Start/end sourced from a row that is missing; upstream parsing returned NaT for an unparseable date string; timezone localization of an ambiguous/nonexistent time yielded NaT.
Related errors
- periods must be an integer, got {periods}
- Must provide freq argument if no data is supplied
- Of the four parameters: start, end, periods, and freq, exact
- Inferred frequency {inferred} from passed values does not co
- Passed data is timezone-aware, incompatible with 'tz=None'.
AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07).
Data as JSON: /api/errors/d009742cfc2edad8.
Report an issue: GitHub.