pandas-dev/pandas · error · TypeError

Cannot convert tz-naive timestamps, use tz_localize to…

Error message

Cannot convert tz-naive timestamps, use tz_localize to localize

What it means

Raised (as TypeError) by ArrowExtensionArray._dt_tz_convert when the array's pyarrow timestamp type has no timezone (tz is None). Converting timezones is only meaningful for tz-aware data; for tz-naive data you must first assign (localize to) a timezone. The message points the user at tz_localize.

Solutions

  1. Localize first: `ser.dt.tz_localize('UTC').dt.tz_convert('US/Eastern')`.
  2. Inspect `ser.dtype.pyarrow_dtype.tz` — if None, you need tz_localize, not tz_convert.

Example fix

# before
ser.dt.tz_convert('US/Eastern')      # ser is tz-naive
# after
ser.dt.tz_localize('UTC').dt.tz_convert('US/Eastern')
Defensive patterns

Strategy: validation

Validate before calling

if ser.dtype.pyarrow_dtype.tz is None:
    raise TypeError("Series is tz-naive; call tz_localize before tz_convert")
ser.dt.tz_convert(tz)

Type guard

def is_arrow_tz_aware(ser) -> bool:
    t = getattr(ser.dtype, "pyarrow_dtype", None)
    return t is not None and getattr(t, "tz", None) is not None

Try / catch

try:
    out = ser.dt.tz_convert(tz)
except TypeError as e:
    if "tz-naive" in str(e):
        out = ser.dt.tz_localize('UTC').dt.tz_convert(tz)
    else:
        raise

Prevention

When it happens

Trigger: Calling `ser.dt.tz_convert('UTC')` on a Series whose dtype is `timestamp[ns][pyarrow]` with no timezone attached.

Common situations: Forgetting that tz_convert is the second step (after tz_localize); reading tz-naive arrow data and immediately trying to convert to UTC.

Related errors


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

Appendix: source

Thrown at pandas/core/arrays/arrow/array.py:4307

            "shift_backward": "earliest",
            "shift_forward": "latest",
        }.get(
            nonexistent,  # type: ignore[arg-type]
            None,
        )
        if nonexistent_pa is None:
            raise NotImplementedError(f"{nonexistent=} is not supported")
        if tz is None:
            result = pc.local_timestamp(self._pa_array)
        else:
            result = pc.assume_timezone(
                self._pa_array, str(tz), ambiguous=ambiguous, nonexistent=nonexistent_pa
            )
        return self._from_pyarrow_array(result)

    def _dt_tz_convert(self, tz) -> Self:
        if self.dtype.pyarrow_dtype.tz is None:
            raise TypeError(
                "Cannot convert tz-naive timestamps, use tz_localize to localize"
            )
        current_unit = self.dtype.pyarrow_dtype.unit
        result = self._pa_array.cast(pa.timestamp(current_unit, tz))
        return self._from_pyarrow_array(result)


def transpose_homogeneous_pyarrow(
    arrays: Sequence[ArrowExtensionArray],
) -> list[ArrowExtensionArray]:
    """Transpose arrow extension arrays in a list, but faster.

    Input should be a list of arrays of equal length and all have the same
    dtype. The caller is responsible for ensuring validity of input data.
    """
    arrays = list(arrays)
    nrows, ncols = len(arrays[0]), len(arrays)
    indices = np.arange(nrows * ncols).reshape(ncols, nrows).T.reshape(-1)

View on GitHub (pinned to 3b7651241d)