I got a strange error in my pytest after some Dependabot updates.
In my case it's triggered by an non-existent MinIO bucket exception.
None of the updates made by Dependabot were in MinIO or Python (but "pytest-asyncio" was updated, to 1.2).
Some parts of the error log:
> raise response_error
E minio.error.S3Error: S3 operation failed; code: NoSuchBucket, message: Volume not found, resource: None, request_id: None, host_id: None
/Users/ar/Library/Caches/pypoetry/virtualenvs/classifier-api-cL9y8buz-py3.13/lib/python3.13/site-packages/minio/api.py:427: S3Error
During handling of the above exception, another exception occurred:
self = <contextlib._GeneratorContextManager object at 0x1178816a0>, typ = <class 'minio.error.S3Error'>
value = S3Error(response=<urllib3.response.HTTPResponse object at 0x116243820>, code='NoSuchBucket', message='Volume not found', resource=None, request_id=None, host_id=None, bucket_name=None, object_name=None)
traceback = <traceback object at 0x11600b480>
def __exit__(self, typ, value, traceback):
I think the relevant part of the error is this last bit:
except BaseException as exc:
# only re-raise if it's *not* the exception that was
# passed to throw(), because __exit__() must not raise
# an exception unless __exit__() itself failed. But throw()
# has to raise the exception to signal propagation, so this
# fixes the impedance mismatch between the throw() protocol
# and the __exit__() protocol.
if exc is not value:
raise
> exc.__traceback__ = traceback
^^^^^^^^^^^^^^^^^
E dataclasses.FrozenInstanceError: cannot assign to field '__traceback__'
I assume the exc (should be the one from MinIO) here is now frozen and can't be modified any more. But where was it frozen? By whom? There was no change in the MinIO libs.
The quick fix was to fix the missing bucket in MinIO, but that's not the cause of that exception error.
EDIT:
Full error from a pytest run:
__________________________________________ ERROR at teardown of TestPostSubmitEndpoint.test_par_document_is_created_correctly_post __________________________________________
self = <contextlib._GeneratorContextManager object at 0x1178816a0>, typ = <class 'minio.error.S3Error'>
value = S3Error(response=<urllib3.response.HTTPResponse object at 0x116243820>, code='NoSuchBucket', message='Volume not found', resource=None, request_id=None, host_id=None, bucket_name=None, object_name=None)
traceback = <traceback object at 0x11600b480>
def __exit__(self, typ, value, traceback):
if typ is None:
try:
next(self.gen)
except StopIteration:
return False
else:
try:
raise RuntimeError("generator didn't stop")
finally:
self.gen.close()
else:
if value is None:
# Need to force instantiation so we can reliably
# tell if we get the same exception back
value = typ()
try:
> self.gen.throw(value)
/opt/homebrew/Cellar/[email protected]/3.13.9_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/contextlib.py:162:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/Users/ar/Library/Caches/pypoetry/virtualenvs/classifier-api-cL9y8buz-py3.13/lib/python3.13/site-packages/pytest_asyncio/plugin.py:335: in finalizer
runner.run(async_finalizer(), context=context)
/opt/homebrew/Cellar/[email protected]/3.13.9_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/runners.py:118: in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/opt/homebrew/Cellar/[email protected]/3.13.9_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/base_events.py:725: in run_until_complete
return future.result()
^^^^^^^^^^^^^^^
/Users/ar/Library/Caches/pypoetry/virtualenvs/classifier-api-cL9y8buz-py3.13/lib/python3.13/site-packages/pytest_asyncio/plugin.py:327: in async_finalizer
await gen_obj.__anext__()
tests/routers/conftest.py:121: in minio_client
for obj in minio_client.list_objects(settings.minio_bucket, recursive=True):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/Users/ar/Library/Caches/pypoetry/virtualenvs/classifier-api-cL9y8buz-py3.13/lib/python3.13/site-packages/minio/api.py:3323: in _list_objects
response = self._execute(
/Users/ar/Library/Caches/pypoetry/virtualenvs/classifier-api-cL9y8buz-py3.13/lib/python3.13/site-packages/minio/api.py:441: in _execute
region = self._get_region(bucket_name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/Users/ar/Library/Caches/pypoetry/virtualenvs/classifier-api-cL9y8buz-py3.13/lib/python3.13/site-packages/minio/api.py:498: in _get_region
response = self._url_open(
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <minio.api.Minio object at 0x116248440>, method = 'GET', region = 'us-east-1', bucket_name = 'nuke', object_name = None, body = None
headers = {'Authorization': 'AWS4-HMAC-SHA256 Credential=minio/20251105/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-cont...m64) minio-py/7.2.18', 'x-amz-content-sha256': 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855', ...}
query_params = {'location': ''}, preload_content = True, no_body_trace = False
def _url_open(
self,
method: str,
region: str,
bucket_name: Optional[str] = None,
object_name: Optional[str] = None,
body: Optional[bytes] = None,
headers: Optional[DictType] = None,
query_params: Optional[DictType] = None,
preload_content: bool = True,
no_body_trace: bool = False,
) -> BaseHTTPResponse:
"""Execute HTTP request."""
creds = self._provider.retrieve() if self._provider else None
url = self._base_url.build(
method=method,
region=region,
bucket_name=bucket_name,
object_name=object_name,
query_params=query_params,
)
headers, date = self._build_headers(url.netloc, headers, body, creds)
if creds:
headers = sign_v4_s3(
method=method,
url=url,
region=region,
headers=headers,
credentials=creds,
content_sha256=cast(str, headers.get("x-amz-content-sha256")),
date=date,
)
if self._trace_stream:
self._trace_stream.write("---------START-HTTP---------\n")
query = ("?" + url.query) if url.query else ""
self._trace_stream.write(f"{method} {url.path}{query} HTTP/1.1\n")
self._trace_stream.write(
headers_to_strings(headers, titled_key=True),
)
self._trace_stream.write("\n")
if not no_body_trace and body is not None:
self._trace_stream.write("\n")
self._trace_stream.write(
body.decode() if isinstance(body, bytes) else str(body),
)
self._trace_stream.write("\n")
self._trace_stream.write("\n")
http_headers = HTTPHeaderDict()
for key, value in (headers or {}).items():
if isinstance(value, (list, tuple)):
for val in value:
http_headers.add(key, val)
else:
http_headers.add(key, value)
response = self._http.urlopen(
method,
urlunsplit(url),
body=body,
headers=http_headers,
preload_content=preload_content,
)
if self._trace_stream:
self._trace_stream.write(f"HTTP/1.1 {response.status}\n")
self._trace_stream.write(
headers_to_strings(response.headers),
)
self._trace_stream.write("\n")
if response.status in [200, 204, 206]:
if self._trace_stream:
if preload_content:
self._trace_stream.write("\n")
self._trace_stream.write(response.data.decode())
self._trace_stream.write("\n")
self._trace_stream.write("----------END-HTTP----------\n")
return response
response.read(cache_content=True)
if not preload_content:
response.release_conn()
if self._trace_stream and method != "HEAD" and response.data:
self._trace_stream.write(response.data.decode())
self._trace_stream.write("\n")
if (
method != "HEAD" and
"application/xml" not in response.headers.get(
"content-type", "",
).split(";")
):
if self._trace_stream:
self._trace_stream.write("----------END-HTTP----------\n")
if response.status == 304 and not response.data:
raise ServerError(
f"server failed with HTTP status code {response.status}",
response.status,
)
raise InvalidResponseError(
response.status,
cast(str, response.headers.get("content-type")),
response.data.decode() if response.data else None,
)
if not response.data and method != "HEAD":
if self._trace_stream:
self._trace_stream.write("----------END-HTTP----------\n")
raise InvalidResponseError(
response.status,
response.headers.get("content-type"),
None,
)
response_error = S3Error.fromxml(response) if response.data else None
if self._trace_stream:
self._trace_stream.write("----------END-HTTP----------\n")
error_map = {
301: lambda: self._handle_redirect_response(
method, response, bucket_name, True,
),
307: lambda: self._handle_redirect_response(
method, response, bucket_name, True,
),
400: lambda: self._handle_redirect_response(
method, response, bucket_name, True,
),
403: lambda: ("AccessDenied", "Access denied"),
404: lambda: (
("NoSuchKey", "Object does not exist")
if object_name
else ("NoSuchBucket", "Bucket does not exist")
if bucket_name
else ("ResourceNotFound", "Request resource not found")
),
405: lambda: (
"MethodNotAllowed",
"The specified method is not allowed against this resource",
),
409: lambda: (
("NoSuchBucket", "Bucket does not exist")
if bucket_name
else ("ResourceConflict", "Request resource conflicts"),
),
501: lambda: (
"MethodNotAllowed",
"The specified method is not allowed against this resource",
),
}
if not response_error:
func = error_map.get(response.status)
code, message = func() if func else (None, None)
if not code:
raise ServerError(
f"server failed with HTTP status code {response.status}",
response.status,
)
response_error = S3Error(
response=response,
code=cast(str, code),
message=cast(Union[str, None], message),
resource=url.path,
request_id=response.headers.get("x-amz-request-id"),
host_id=response.headers.get("x-amz-id-2"),
bucket_name=bucket_name,
object_name=object_name,
)
if response_error.code in ["NoSuchBucket", "RetryHead"]:
if bucket_name is not None:
self._region_map.pop(bucket_name, None)
> raise response_error
E minio.error.S3Error: S3 operation failed; code: NoSuchBucket, message: Volume not found, resource: None, request_id: None, host_id: None
/Users/ar/Library/Caches/pypoetry/virtualenvs/classifier-api-cL9y8buz-py3.13/lib/python3.13/site-packages/minio/api.py:427: S3Error
During handling of the above exception, another exception occurred:
self = <contextlib._GeneratorContextManager object at 0x1178816a0>, typ = <class 'minio.error.S3Error'>
value = S3Error(response=<urllib3.response.HTTPResponse object at 0x116243820>, code='NoSuchBucket', message='Volume not found', resource=None, request_id=None, host_id=None, bucket_name=None, object_name=None)
traceback = <traceback object at 0x11600b480>
def __exit__(self, typ, value, traceback):
if typ is None:
try:
next(self.gen)
except StopIteration:
return False
else:
try:
raise RuntimeError("generator didn't stop")
finally:
self.gen.close()
else:
if value is None:
# Need to force instantiation so we can reliably
# tell if we get the same exception back
value = typ()
try:
self.gen.throw(value)
except StopIteration as exc:
# Suppress StopIteration *unless* it's the same exception that
# was passed to throw(). This prevents a StopIteration
# raised inside the "with" statement from being suppressed.
return exc is not value
except RuntimeError as exc:
# Don't re-raise the passed in exception. (issue27122)
if exc is value:
exc.__traceback__ = traceback
return False
# Avoid suppressing if a StopIteration exception
# was passed to throw() and later wrapped into a RuntimeError
# (see PEP 479 for sync generators; async generators also
# have this behavior). But do this only if the exception wrapped
# by the RuntimeError is actually Stop(Async)Iteration (see
# issue29692).
if (
isinstance(value, StopIteration)
and exc.__cause__ is value
):
value.__traceback__ = traceback
return False
raise
except BaseException as exc:
# only re-raise if it's *not* the exception that was
# passed to throw(), because __exit__() must not raise
# an exception unless __exit__() itself failed. But throw()
# has to raise the exception to signal propagation, so this
# fixes the impedance mismatch between the throw() protocol
# and the __exit__() protocol.
if exc is not value:
raise
> exc.__traceback__ = traceback
^^^^^^^^^^^^^^^^^
E dataclasses.FrozenInstanceError: cannot assign to field '__traceback__'
/opt/homebrew/Cellar/[email protected]/3.13.9_1/Frameworks/Python.framework/Versions/3.13/lib/python3.13/contextlib.py:195: FrozenInstanceError
.__traceback__assignment failed? You have a stacktrace, but you've not yet shared it with us.