pandas-dev/pandas · error · TypeError
Cannot convert tz-naive timestamps, use tz_localize to local
Error message
Cannot convert tz-naive timestamps, use tz_localize to localize
What it means
Raised by ArrowExtensionArray._dt_tz_convert when the Series timestamps are tz-naive (pyarrow timestamp type has tz=None). tz_convert requires an existing timezone to convert from; you must first localize naive timestamps. Raised as TypeError and reached through Series.dt.tz_convert() on a tz-naive timestamp[pyarrow] Series.
Source
Thrown at pandas/core/arrays/arrow/array.py:4279
"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 71959b8cb9)
Solutions
- Localize first: `s.dt.tz_localize("UTC").dt.tz_convert("US/Eastern")`.
- Check `s.dtype.pyarrow_dtype.tz` before calling tz_convert; if None, call tz_localize instead.
- Load the data with explicit tz: parse with `pd.to_datetime(s, utc=True)` if source is UTC.
- Use `s.dt.tz` (None for naive) as a guard in pipeline code.
Example fix
# before
s = pd.Series(..., dtype="timestamp[us][pyarrow]") # tz-naive
s.dt.tz_convert("UTC") # TypeError
# after
s.dt.tz_localize("UTC").dt.tz_convert("US/Eastern") Defensive patterns
Strategy: type-guard
Validate before calling
def is_tz_aware_pyarrow(s) -> bool:
pa_dt = getattr(s.dtype, "pyarrow_dtype", None)
return pa_dt is not None and getattr(pa_dt, "tz", None) is not None
def safe_tz_convert(s, tz):
if not is_tz_aware_pyarrow(s):
raise TypeError("Series is tz-naive; call tz_localize(tz) first")
return s.dt.tz_convert(tz) Type guard
import pyarrow as pa
def is_tz_aware_pyarrow(s) -> bool:
pa_dt = getattr(s.dtype, "pyarrow_dtype", None)
return pa_dt is not None and pa.types.is_timestamp(pa_dt) and pa_dt.tz is not None Try / catch
try:
out = s.dt.tz_convert(tz)
except TypeError:
out = s.dt.tz_localize("UTC").dt.tz_convert(tz) Prevention
- Check s.dtype.pyarrow_dtype.tz before tz_convert; None means call tz_localize instead.
- Load timestamp data with explicit tz (e.g. pd.to_datetime(s, utc=True)).
- Document localize-vs-convert decision in ETL helpers.
When it happens
Trigger: Calling `s.dt.tz_convert("UTC")` on a Series whose dtype is `timestamp[us][pyarrow]` with no tz. Common after loading Parquet/Arrow data that stored timezone-naive timestamps.
Common situations: Assuming a column is tz-aware when it is actually naive; chaining tz_convert where tz_localize was needed; data ingestion stripping tz metadata.
Related errors
- '{type(self).__name__}' object is not iterable
- __invert__ is not supported for string dtypes
- unary '-' not supported for dtype '{self.dtype}'
- operation '{op.__name__}' not supported for dtype '{self.dty
- Can only string multiply by an integer.
AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07).
Data as JSON: /api/errors/43021e75a4f0f5b4.
Report an issue: GitHub.