From 999f8ddb4f5606f72b434faf63baa610ead76a6f Mon Sep 17 00:00:00 2001 From: aemous Date: Mon, 17 Aug 2026 17:48:43 -0400 Subject: [PATCH] Fix race condition with S3Express multi-file transfers. --- .changes/next-release/bugfix-s3-3139.json | 5 +++++ awscli/botocore/utils.py | 8 +++++++- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 .changes/next-release/bugfix-s3-3139.json diff --git a/.changes/next-release/bugfix-s3-3139.json b/.changes/next-release/bugfix-s3-3139.json new file mode 100644 index 000000000000..b9fb26324e7e --- /dev/null +++ b/.changes/next-release/bugfix-s3-3139.json @@ -0,0 +1,5 @@ +{ + "type": "bugfix", + "category": "``s3``", + "description": "Fixes a race condition where CreateSession is called multiple times across different threads during an S3-Express recursive upload/download." +} diff --git a/awscli/botocore/utils.py b/awscli/botocore/utils.py index a3ac03e745e8..8915354cc3b8 100644 --- a/awscli/botocore/utils.py +++ b/awscli/botocore/utils.py @@ -25,6 +25,7 @@ import secrets import socket import string +import threading import time import uuid import warnings @@ -1483,11 +1484,16 @@ class S3ExpressIdentityCache(IdentityCache): def __init__(self, client, credential_cls): self._client = client self._credential_cls = credential_cls + self._lock = threading.Lock() @functools.lru_cache(maxsize=100) - def get_credentials(self, bucket): + def _get_credentials_cached(self, bucket): return super().get_credentials(bucket=bucket) + def get_credentials(self, bucket): + with self._lock: + return self._get_credentials_cached(bucket) + def build_refresh_callback(self, bucket): def refresher(): response = self._client.create_session(Bucket=bucket)