# What changed in pyathena

> 34 changes in pyathena 3 to 4 from its official release notes, upgrade guides and advisories: 21 breaking, 2 new APIs and options. Each one cited. Sources checked 2026-10-25. HTML: https://unstale.sh/pypi/pyathena/

To see which of these apply to a repository, run `npx @unstale/cli init` in it.

## pyathena 4

### Breaking changes

- Blocking calls run in filesystem and per-file pools of up to max_workers threads instead of the event loop's default executor; non-positive values raise ValueError. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)
- CSV dates are now read as date32 instead of timestamp[ms]; as_polars() returns pl.Date. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)
- AioPandasCursor.as_pandas() is now a coroutine, must be awaited, and always returns a DataFrame. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)
- After closing an AsyncCursor, pending execute(), description(), and poll() futures raise ProgrammingError; the query is not cancelled. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)
- description(query_id) now reads result metadata or an UNLOAD Parquet schema without reading result rows; custom result sets must customize their metadata reader instead. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)
- Methods on a closed cursor now raise ProgrammingError("Cursor is closed.") instead of returning empty values or running a new query. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)
- Fetch, iteration, conversion, and chunk methods on a closed result set now raise ProgrammingError instead of returning empty results. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)
- Conversion failures in every cursor now raise DataError naming the column; pandas whole reads no longer wrap that failure in OperationalError. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)
- After a fetch fails, subsequent fetchone(), fetchmany(), and fetchall() calls raise ProgrammingError; invalid arguments do not count as failed fetches. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)
- Polars 2.0 describes UNLOAD map columns as map with dict values, and ArrowCursor.as_polars() returns them as pl.Map. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)
- Calling next() on an asyncio cursor or result set now raises TypeError rather than returning an un-awaited coroutine. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)
- Failed reads of UNLOAD manifests and Spark calculation output now raise OperationalError instead of botocore exceptions. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)
- Temporal dtypes now depend only on the Athena type: timestamps use microseconds, zoned timestamps use UTC, and UNLOAD dates use datetime64[us]. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)
- With Polars 2.0, new_columns used together with schema_overrides must name every column; a schema passed to execute() is used as given. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)
- Multipart writes keep at most max_workers parts in flight; failures surface on the next write(), flush(), or close(), which closes without committing. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)
- commit() raises S3MultipartStateError after discard() or a failed write or commit. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)
- S3File.multipart_upload is now read-only. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)
- S3File.multipart_upload_parts has been removed. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)
- With the default flavor="spark", table column names replace spaces, tabs, newlines, and ,;{}()= with underscores to match Parquet names. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)
- tzdata>=2024.2 is required for timestamp with time zone values; unknown or ambiguous daylight-saving zones now raise DataError. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)

### New APIs and options

- Cursors now expose an is_closed property. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)
- AioPandasCursor and AioPolarsCursor add async iter_chunks() for reading chunks one at a time. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)

### Performance

- With Polars 1.x, unchunked CSV reads no longer hold a second copy of the result file, reducing peak memory to the level of 4.1.0. (4.2.1) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.1)
- Temporal values are converted faster across Cursor, DictCursor, S3FSCursor, ArrowCursor, PolarsCursor, and PandasCursor reads. (4.2.1) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.1)

### Behavior and default changes

- For unchunked CSV reads, a non-UTF-8 encoding is not decoded and causes OperationalError, matching 4.1.0 behavior. (4.2.1) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.1)
- Temporal converters now use fromisoformat(), accepting more ISO 8601 forms; text from other sources that previously raised ValueError now converts. (4.2.1) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.1)
- threadsafety = 2 declares that calling one cursor or result set from several threads is not supported; use the query ID to stop a query from another thread. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)

### Fixes and other changes

- PandasCursor no longer fails when parse_dates omits a time column or index_col names a time column. (4.2.1) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.1)
- Python 3.15 is now supported. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)
- completed_parts returns uploaded or copied parts in part-number order. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)
- PandasCursor, ArrowCursor, and PolarsCursor read UNLOAD timestamps at microsecond precision at every nesting level, including chunked reads. (4.2.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v4.2.0)

## pyathena 3

### Breaking changes

- Cursor descriptions report Polars 2.0 UNLOAD map columns as map, and structs containing lists as row; with Polars 1.x, maps remain array. (3.40.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v3.40.0)

### Fixes and other changes

- With unload=true, connection pings no longer fail with MISSING_COLUMN_NAME; they run SELECT 1 through a plain Cursor while SQLAlchemy retains its failed-ping handling. (3.40.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v3.40.0)
- PolarsCursor and AioPolarsCursor work with Polars 2.0: CSV results are read through PyAthena's S3 filesystem and passed to Polars as bytes, also supporting Polars 1.x. (3.40.0) — [release notes](https://github.com/pyathena-dev/PyAthena/releases/tag/v3.40.0)
