"Error handling" docs are right for S3 but wrong for SES
Describe the issue
The docs say:
Parsing for error responses uses the same exact methodology outlined in the low-level client section. Catching exceptions through the client’s exceptions property is slightly different, as you’ll need to access the client’s meta property to get to the exceptions.
client.meta.client.exceptions.SomeServiceExceptionUsing Amazon S3 as an example resource service, you can use the client’s exception property to catch the BucketAlreadyExists exception. And you can still parse the error response to get the bucket name that’s passed in the original request.
import botocore import boto3 client = boto3.resource('s3') try: client.create_bucket(BucketName='amzn-s3-demo-bucket') except client.meta.client.exceptions.BucketAlreadyExists as err: print("Bucket {} already exists!".format(err.response['Error']['BucketName'])) raise err
This works for S3 client. But not for SES client:
In [26]: ses = boto3.client("ses")
In [27]: ses.meta.client.exceptions
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[27], line 1
----> 1 ses.meta.client.exceptions
AttributeError: 'ClientMeta' object has no attribute 'client'
For SES client I can do:
In [28]: ses.exceptions
Out[28]: <botocore.errorfactory.SESExceptions at 0x125410fd0>
But that doesn't work for S3:
In [30]: client = boto3.resource('s3')
...:
In [32]: client.exceptions
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[32], line 1
----> 1 client.exceptions
AttributeError: 's3.ServiceResource' object has no attribute 'exceptions'
So it seems that the different clients are inconsistent, and the docs need to reflect this.
Links
https://boto3.amazonaws.com/v1/documentation/api/latest/guide/error-handling.html
I'm using boto3==1.38.17 on Python 3.11
Hello @anentropic, thanks for reaching out. There are two ways to interact to AWS services using boto, its either Client or Resources (Client: https://boto3.amazonaws.com/v1/documentation/api/latest/guide/clients.html , Resources: https://boto3.amazonaws.com/v1/documentation/api/latest/guide/resources.html ) . For handling errors and as shown by the documentation https://boto3.amazonaws.com/v1/documentation/api/latest/guide/error-handling.html#error-handling , it shows how to handle client errors and resources errors.
If you are using a client, then you must use a client error handing. If you are using a resource, then you must use a client error handling. Below shows an example:
import boto3
from botocore.exceptions import ClientError
ses = boto3.client('ses')
try:
# Use an invalid email address to trigger an exception
ses.verify_email_identity(EmailAddress='not-a-valid-email')
print("Operation succeeded")
except ses.exceptions.MessageRejected as e:
print(f"Message was rejected: {e}")
except ses.exceptions.InvalidParameterValue as e:
print(f"Invalid parameter: {e}")
except ClientError as e:
print(f"Other error occurred: {e.response['Error']['Code']}: {e.response['Error']['Message']}")
OUTPUT:
Traceback (most recent call last):
File "/Users/x/github_issues/4534/examples.py", line 8, in <module>
ses.verify_email_identity(EmailAddress='not-a-valid-email')
~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/x/.venv/lib/python3.13/site-packages/botocore/client.py", line 598, in _api_call
return self._make_api_call(operation_name, kwargs)
~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/x/.venv/lib/python3.13/site-packages/botocore/context.py", line 123, in wrapper
return func(*args, **kwargs)
File "/Users/x/.venv/lib/python3.13/site-packages/botocore/client.py", line 1061, in _make_api_call
raise error_class(parsed_response, operation_name)
botocore.exceptions.ClientError: An error occurred (InvalidParameterValue) when calling the VerifyEmailIdentity operation: Invalid email address<not-a-valid-email>.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/Users/x/github_issues/4534/examples.py", line 14, in <module>
except ses.exceptions.InvalidParameterValue as e:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/x/.venv/lib/python3.13/site-packages/botocore/errorfactory.py", line 51, in __getattr__
raise AttributeError(
...<2 lines>...
)
AttributeError: <botocore.errorfactory.SESExceptions object at 0x107315d30> object has no attribute InvalidParameterValue. Valid exceptions are: AccountSendingPausedException, AlreadyExistsException, CannotDeleteException, ConfigurationSetAlreadyExistsException, ConfigurationSetDoesNotExistException, ConfigurationSetSendingPausedException, CustomVerificationEmailInvalidContentException, CustomVerificationEmailTemplateAlreadyExistsException, CustomVerificationEmailTemplateDoesNotExistException, EventDestinationAlreadyExistsException, EventDestinationDoesNotExistException, FromEmailAddressNotVerifiedException, InvalidCloudWatchDestinationException, InvalidConfigurationSetException, InvalidDeliveryOptionsException, InvalidFirehoseDestinationException, InvalidLambdaFunctionException, InvalidPolicyException, InvalidRenderingParameterException, InvalidS3ConfigurationException, InvalidSNSDestinationException, InvalidSnsTopicException, InvalidTemplateException, InvalidTrackingOptionsException, LimitExceededException, MailFromDomainNotVerifiedException, MessageRejected, MissingRenderingAttributeException, ProductionAccessNotGrantedException, RuleDoesNotExistException, RuleSetDoesNotExistException, TemplateDoesNotExistException, TrackingOptionsAlreadyExistsException, TrackingOptionsDoesNotExistException
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
import boto3
from botocore.exceptions import ClientError
# Create an S3 resource
s3 = boto3.resource('s3')
try:
# Try to get an object that doesn't exist
obj = s3.Object('my-unique-bucket-name', 'non-existent-key')
content = obj.get() # This will fail with NoSuchKey
print(f"Content: {content}")
except s3.meta.client.exceptions.NoSuchKey as e:
print(f"Caught specific exception: Object doesn't exist: {e}")
except ClientError as e:
print(f"Caught generic exception: {e}")
print(f"Error code: {e.response['Error']['Code']}")
OUTPUT:
Caught generic exception: An error occurred (AccessDenied) when calling the GetObject operation: Access Denied
Error code: AccessDenied
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
import boto3
from botocore.exceptions import ClientError
# Create an S3 client
s3_client = boto3.client('s3')
try:
# Try to get an object that doesn't exist
response = s3_client.get_object(
Bucket='my-unique-bucket-name',
Key='non-existent-key'
)
print(f"Content: {response}")
except s3_client.exceptions.NoSuchKey as e:
print(f"Caught specific exception: Object doesn't exist: {e}")
except ClientError as e:
print(f"Caught generic exception: {e}")
print(f"Error code: {e.response['Error']['Code']}")
OUTPUT:
Caught generic exception: An error occurred (AccessDenied) when calling the GetObject operation: Access Denied
Error code: AccessDenied
If using a resource error handling to a client and vice versa, it will not work. As shown below and as what you have tested:
import boto3
from botocore.exceptions import ClientError
s3_client = boto3.client('s3')
try:
response = s3_client.get_object(
Bucket='my-unique-bucket-name',
Key='non-existent-key'
)
except s3_client.meta.client.exceptions.NoSuchKey as e:
print(f"Caught specific exception: Object doesn't exist: {e}")
except ClientError as e:
print(f"Caught generic exception: {e}")
OUTPUT:
AttributeError: 'ClientMeta' object has no attribute 'client'
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
import boto3
from botocore.exceptions import ClientError
s3 = boto3.resource('s3')
try:
obj = s3.Object('my-unique-bucket-name', 'non-existent-key')
content = obj.get()
except s3.exceptions.NoSuchKey as e:
print(f"Caught specific exception: Object doesn't exist: {e}")
except ClientError as e:
print(f"Caught generic exception: {e}")
OUTPUT:
AttributeError: 's3.ServiceResource' object has no attribute 'exceptions'
Please let me know if you have any questions. Thanks.
@adev-code thanks, I hadn't noticed the client/resource distinction
I guess this is because not all services have resources? SES doesn't seem to have one
and then S3 docs start with showing a client https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html but then if you know to look for it and scroll down it's there https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#resources
Resources were a popular feature that provided abstractions over the low-level client. These were hand written and only done for a select few services like S3 and DynamoDB. Resources are now “feature frozen” and we advice customers to use low level client instead.
This issue is now closed. Comments on closed issues are hard for our team to see. If you need more assistance, please open a new issue that references this one.