pandas-dev/pandas · error · ValueError

'unit' must be one of 's', 'ms', 'us', 'ns'

Error message

'unit' must be one of 's', 'ms', 'us', 'ns'

What it means

Raised in _generate_range when the unit keyword is not one of the four valid storage resolutions ('s','ms','us','ns'). This controls the i8 precision of the generated datetime64 backing array; coarser/finer aliases are not valid storage units.

Source

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

        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
            start = _maybe_localize_point(start, freq, tz, ambiguous, nonexistent)
            end = _maybe_localize_point(end, freq, tz, ambiguous, nonexistent)

        if freq is not None:

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Pass unit='s'|'ms'|'us'|'ns' to set storage precision.
  2. For minute/hourly spacing use freq='min'|'h' instead of unit.
  3. If you genuinely need coarser-than-second storage, store in 's' and downcast at display time.

Example fix

# before
pd.date_range('2020-01-01', periods=3, freq='D', unit='m')

# after
pd.date_range('2020-01-01', periods=3, freq='min', unit='s')
Defensive patterns

Strategy: validation

Validate before calling

VALID_UNITS = {'s','ms','us','ns'}
if unit not in VALID_UNITS:
    raise ValueError(f'unit must be one of {VALID_UNITS}, got {unit!r}')

Type guard

from typing import Literal
def is_storage_unit(u: str) -> bool:
    return u in {'s','ms','us','ns'}

Try / catch

try:
    pd.date_range(start, periods=3, freq='D', unit=unit)
except ValueError as e:
    if "'unit' must be one of" in str(e):
        pd.date_range(start, periods=3, freq='D', unit='ns')
    else: raise

Prevention

When it happens

Trigger: pd.date_range(..., unit='m'), pd.date_range(..., unit='us5'), pd.date_range(..., unit='ps'), or passing a frequency alias like 'h'/'T' as unit.

Common situations: Confusing the unit= parameter (storage resolution) with the freq= parameter (grid spacing). Copying unit strings from numpy dtype expressions like 'datetime64[m]'. Wanting minute/hour spacing.

Related errors


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