diff --git a/HISTORY.md b/HISTORY.md index 289794af..5a05d913 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -21,6 +21,7 @@ - Changed `S3Client._get_metadata` to read object metadata with `HeadObject` instead of `GetObject`, so `stat`, `etag`, and `size` no longer open the object body. Also fixes a `KeyError` on `ContentLength` against S3-compatible gateways that drop `Content-Length` from `GetObject` responses. (Issue [#564](https://github.com/drivendataorg/cloudpathlib/issues/564), PR [#565](https://github.com/drivendataorg/cloudpathlib/pull/565)) - Added a `lazy` keyword argument to `CloudPath.walk`. By default (`lazy=False`) the existing fast behavior is preserved: the whole subtree is fetched up front with a single recursive listing. Passing `lazy=True` lists each directory on demand so that, when `top_down=True`, callers can prune subdirectories by modifying `dirnames` in-place (à la `os.walk` / `Path.walk`) to skip fetching the contents of those subtrees entirely — dramatically reducing API calls for large, sparsely-traversed trees. (Issue [#518](https://github.com/drivendataorg/cloudpathlib/issues/518)) - Fixed S3 copy and move operations to forward copy-specific extra args such as `CopySourceSSECustomerKey`, and added `addressing_style="virtual"` support for `S3Client` (Issues [#500](https://github.com/drivendataorg/cloudpathlib/issues/500), [#527](https://github.com/drivendataorg/cloudpathlib/issues/527)) +- Fix `KeyError` on `ETag` against S3-compatible gateways that omit `ETag` from `HeadObject` responses. (Issue [#582](https://github.com/drivendataorg/cloudpathlib/issues/582), PR [#583](https://github.com/drivendataorg/cloudpathlib/pull/583)) ## v0.24.0 (2026-04-29) - Added support for S3 Multi-Region Access Point (MRAP) URLs in `S3Path` (Issue [#556](https://github.com/drivendataorg/cloudpathlib/issues/556), PR [#557](https://github.com/drivendataorg/cloudpathlib/pull/557)) diff --git a/cloudpathlib/s3/s3client.py b/cloudpathlib/s3/s3client.py index 9fa4bd75..db34177d 100644 --- a/cloudpathlib/s3/s3client.py +++ b/cloudpathlib/s3/s3client.py @@ -154,7 +154,7 @@ def _get_metadata(self, cloud_path: S3Path) -> Dict[str, Any]: return { "last_modified": data["LastModified"], "size": data["ContentLength"], - "etag": data["ETag"], + "etag": data.get("ETag", None), "content_type": data.get("ContentType", None), "extra": data["Metadata"], }