pandas-dev/pandas · error · ValueError

'value' should be a Timedelta.

Error message

'value' should be a Timedelta.

What it means

TimedeltaArray._unbox_scalar raises ValueError when the supplied value is neither a Timedelta nor NaT. Operations that need to compare or set a scalar against the array require the scalar to be a Timedelta (or NaT) so the resolution can be aligned; passing int, float, str, or datetime is rejected.

Solutions

  1. Wrap the value in pd.Timedelta first: pd.Timedelta(value).
  2. Use pd.NaT for missing scalars.
  3. If the value is an integer in a known unit, construct pd.Timedelta(value, unit='ns').

Example fix

// before
arr._unbox_scalar(5)  # ValueError
// after
arr._unbox_scalar(pd.Timedelta(5, unit='ns'))
Defensive patterns

Strategy: type-guard

Validate before calling

def unbox_td_scalar(arr, value):
    import pandas as pd
    if not isinstance(value, (pd.Timedelta, type(pd.NaT))):
        value = pd.Timedelta(value)
    return arr._unbox_scalar(value)

Type guard

def is_timedelta_or_nat(value) -> bool:
    import pandas as pd
    return isinstance(value, pd.Timedelta) or value is pd.NaT

Try / catch

try:
    arr._unbox_scalar(value)
except ValueError as e:
    if "should be a Timedelta" in str(e):
        arr._unbox_scalar(pd.Timedelta(value))
    else:
        raise

Prevention

When it happens

Trigger: Internal call _unbox_scalar(5) or _unbox_scalar('1 day') on a TimedeltaArray; comparison ops that route non-Timedelta scalars through _unbox_scalar; setitem with a Python int.

Common situations: Passing an integer meant as nanoseconds directly; passing a string instead of pd.Timedelta(...); using a datetime where a timedelta was expected.

Related errors


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

Appendix: source

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

        if freq is not None:
            index = generate_regular_range(start, end, periods, freq, unit=unit)
        else:
            index = np.linspace(start._value, end._value, periods).astype("i8")

        if not left_closed:
            index = index[1:]
        if not right_closed:
            index = index[:-1]

        td64values = index.view(f"m8[{unit}]")
        return cls._simple_new(td64values, dtype=td64values.dtype)

    # ----------------------------------------------------------------
    # DatetimeLike Interface

    def _unbox_scalar(self, value) -> np.timedelta64:
        if not isinstance(value, self._scalar_type) and value is not NaT:
            raise ValueError("'value' should be a Timedelta.")
        self._check_compatible_with(value)
        if value is NaT:
            return np.timedelta64(value._value, self.unit)
        else:
            #  error: Incompatible return value type (got "timedelta64[timedelta |
            # int | None] | datetime64[date | int | None]",
            # expected "timedelta64[timedelta | int | None]")
            return value.as_unit(self.unit, round_ok=False).asm8  # type: ignore[return-value]

    def _scalar_from_string(self, value) -> Timedelta | NaTType:
        return Timedelta(value)

    def _check_compatible_with(self, other) -> None:
        # we don't have anything to validate.
        pass

    # ----------------------------------------------------------------
    # Array-Like / EA-Interface Methods

View on GitHub (pinned to 3b7651241d)