Boto3 client error

I am trying to figure how to do proper error handling with boto3. I am trying to create an IAM user: def create_user(username, iam_conn): try: user = iam_conn.create_user(UserName=use...

As a few others already mentioned, you can catch certain errors using the service client (service_client.exceptions.<ExceptionClass>) or resource (service_resource.meta.client.exceptions.<ExceptionClass>), however it is not well documented (also which exceptions belong to which clients). So here is how to get the complete mapping at time of writing (January 2020) in region EU (Ireland) (eu-west-1):

import boto3, pprint

region_name = 'eu-west-1'
session = boto3.Session(region_name=region_name)
exceptions = {
  service: list(boto3.client(service).exceptions._code_to_exception)
  for service in session.get_available_services()
}
pprint.pprint(exceptions, width=20000)

Here is a subset of the pretty large document:

{'acm': ['InvalidArnException', 'InvalidDomainValidationOptionsException', 'InvalidStateException', 'InvalidTagException', 'LimitExceededException', 'RequestInProgressException', 'ResourceInUseException', 'ResourceNotFoundException', 'TooManyTagsException'],
 'apigateway': ['BadRequestException', 'ConflictException', 'LimitExceededException', 'NotFoundException', 'ServiceUnavailableException', 'TooManyRequestsException', 'UnauthorizedException'],
 'athena': ['InternalServerException', 'InvalidRequestException', 'TooManyRequestsException'],
 'autoscaling': ['AlreadyExists', 'InvalidNextToken', 'LimitExceeded', 'ResourceContention', 'ResourceInUse', 'ScalingActivityInProgress', 'ServiceLinkedRoleFailure'],
 'cloudformation': ['AlreadyExistsException', 'ChangeSetNotFound', 'CreatedButModifiedException', 'InsufficientCapabilitiesException', 'InvalidChangeSetStatus', 'InvalidOperationException', 'LimitExceededException', 'NameAlreadyExistsException', 'OperationIdAlreadyExistsException', 'OperationInProgressException', 'OperationNotFoundException', 'StackInstanceNotFoundException', 'StackSetNotEmptyException', 'StackSetNotFoundException', 'StaleRequestException', 'TokenAlreadyExistsException'],
 'cloudfront': ['AccessDenied', 'BatchTooLarge', 'CNAMEAlreadyExists', 'CannotChangeImmutablePublicKeyFields', 'CloudFrontOriginAccessIdentityAlreadyExists', 'CloudFrontOriginAccessIdentityInUse', 'DistributionAlreadyExists', 'DistributionNotDisabled', 'FieldLevelEncryptionConfigAlreadyExists', 'FieldLevelEncryptionConfigInUse', 'FieldLevelEncryptionProfileAlreadyExists', 'FieldLevelEncryptionProfileInUse', 'FieldLevelEncryptionProfileSizeExceeded', 'IllegalFieldLevelEncryptionConfigAssociationWithCacheBehavior', 'IllegalUpdate', 'InconsistentQuantities', 'InvalidArgument', 'InvalidDefaultRootObject', 'InvalidErrorCode', 'InvalidForwardCookies', 'InvalidGeoRestrictionParameter', 'InvalidHeadersForS3Origin', 'InvalidIfMatchVersion', 'InvalidLambdaFunctionAssociation', 'InvalidLocationCode', 'InvalidMinimumProtocolVersion', 'InvalidOrigin', 'InvalidOriginAccessIdentity', 'InvalidOriginKeepaliveTimeout', 'InvalidOriginReadTimeout', 'InvalidProtocolSettings', 'InvalidQueryStringParameters', 'InvalidRelativePath', 'InvalidRequiredProtocol', 'InvalidResponseCode', 'InvalidTTLOrder', 'InvalidTagging', 'InvalidViewerCertificate', 'InvalidWebACLId', 'MissingBody', 'NoSuchCloudFrontOriginAccessIdentity', 'NoSuchDistribution', 'NoSuchFieldLevelEncryptionConfig', 'NoSuchFieldLevelEncryptionProfile', 'NoSuchInvalidation', 'NoSuchOrigin', 'NoSuchPublicKey', 'NoSuchResource', 'NoSuchStreamingDistribution', 'PreconditionFailed', 'PublicKeyAlreadyExists', 'PublicKeyInUse', 'QueryArgProfileEmpty', 'StreamingDistributionAlreadyExists', 'StreamingDistributionNotDisabled', 'TooManyCacheBehaviors', 'TooManyCertificates', 'TooManyCloudFrontOriginAccessIdentities', 'TooManyCookieNamesInWhiteList', 'TooManyDistributionCNAMEs', 'TooManyDistributions', 'TooManyDistributionsAssociatedToFieldLevelEncryptionConfig', 'TooManyDistributionsWithLambdaAssociations', 'TooManyFieldLevelEncryptionConfigs', 'TooManyFieldLevelEncryptionContentTypeProfiles', 'TooManyFieldLevelEncryptionEncryptionEntities', 'TooManyFieldLevelEncryptionFieldPatterns', 'TooManyFieldLevelEncryptionProfiles', 'TooManyFieldLevelEncryptionQueryArgProfiles', 'TooManyHeadersInForwardedValues', 'TooManyInvalidationsInProgress', 'TooManyLambdaFunctionAssociations', 'TooManyOriginCustomHeaders', 'TooManyOriginGroupsPerDistribution', 'TooManyOrigins', 'TooManyPublicKeys', 'TooManyQueryStringParameters', 'TooManyStreamingDistributionCNAMEs', 'TooManyStreamingDistributions', 'TooManyTrustedSigners', 'TrustedSignerDoesNotExist'],
 'cloudtrail': ['CloudTrailARNInvalidException', 'CloudTrailAccessNotEnabledException', 'CloudWatchLogsDeliveryUnavailableException', 'InsufficientDependencyServiceAccessPermissionException', 'InsufficientEncryptionPolicyException', 'InsufficientS3BucketPolicyException', 'InsufficientSnsTopicPolicyException', 'InvalidCloudWatchLogsLogGroupArnException', 'InvalidCloudWatchLogsRoleArnException', 'InvalidEventSelectorsException', 'InvalidHomeRegionException', 'InvalidKmsKeyIdException', 'InvalidLookupAttributesException', 'InvalidMaxResultsException', 'InvalidNextTokenException', 'InvalidParameterCombinationException', 'InvalidS3BucketNameException', 'InvalidS3PrefixException', 'InvalidSnsTopicNameException', 'InvalidTagParameterException', 'InvalidTimeRangeException', 'InvalidTokenException', 'InvalidTrailNameException', 'KmsException', 'KmsKeyDisabledException', 'KmsKeyNotFoundException', 'MaximumNumberOfTrailsExceededException', 'NotOrganizationMasterAccountException', 'OperationNotPermittedException', 'OrganizationNotInAllFeaturesModeException', 'OrganizationsNotInUseException', 'ResourceNotFoundException', 'ResourceTypeNotSupportedException', 'S3BucketDoesNotExistException', 'TagsLimitExceededException', 'TrailAlreadyExistsException', 'TrailNotFoundException', 'TrailNotProvidedException', 'UnsupportedOperationException'],
 'cloudwatch': ['InvalidParameterInput', 'ResourceNotFound', 'InternalServiceError', 'InvalidFormat', 'InvalidNextToken', 'InvalidParameterCombination', 'InvalidParameterValue', 'LimitExceeded', 'MissingParameter'],
 'codebuild': ['AccountLimitExceededException', 'InvalidInputException', 'OAuthProviderException', 'ResourceAlreadyExistsException', 'ResourceNotFoundException'],
 'config': ['InsufficientDeliveryPolicyException', 'InsufficientPermissionsException', 'InvalidConfigurationRecorderNameException', 'InvalidDeliveryChannelNameException', 'InvalidLimitException', 'InvalidNextTokenException', 'InvalidParameterValueException', 'InvalidRecordingGroupException', 'InvalidResultTokenException', 'InvalidRoleException', 'InvalidS3KeyPrefixException', 'InvalidSNSTopicARNException', 'InvalidTimeRangeException', 'LastDeliveryChannelDeleteFailedException', 'LimitExceededException', 'MaxNumberOfConfigRulesExceededException', 'MaxNumberOfConfigurationRecordersExceededException', 'MaxNumberOfDeliveryChannelsExceededException', 'MaxNumberOfRetentionConfigurationsExceededException', 'NoAvailableConfigurationRecorderException', 'NoAvailableDeliveryChannelException', 'NoAvailableOrganizationException', 'NoRunningConfigurationRecorderException', 'NoSuchBucketException', 'NoSuchConfigRuleException', 'NoSuchConfigurationAggregatorException', 'NoSuchConfigurationRecorderException', 'NoSuchDeliveryChannelException', 'NoSuchRetentionConfigurationException', 'OrganizationAccessDeniedException', 'OrganizationAllFeaturesNotEnabledException', 'OversizedConfigurationItemException', 'ResourceInUseException', 'ResourceNotDiscoveredException', 'ValidationException'],
 'dynamodb': ['BackupInUseException', 'BackupNotFoundException', 'ConditionalCheckFailedException', 'ContinuousBackupsUnavailableException', 'GlobalTableAlreadyExistsException', 'GlobalTableNotFoundException', 'IdempotentParameterMismatchException', 'IndexNotFoundException', 'InternalServerError', 'InvalidRestoreTimeException', 'ItemCollectionSizeLimitExceededException', 'LimitExceededException', 'PointInTimeRecoveryUnavailableException', 'ProvisionedThroughputExceededException', 'ReplicaAlreadyExistsException', 'ReplicaNotFoundException', 'RequestLimitExceeded', 'ResourceInUseException', 'ResourceNotFoundException', 'TableAlreadyExistsException', 'TableInUseException', 'TableNotFoundException', 'TransactionCanceledException', 'TransactionConflictException', 'TransactionInProgressException'],
 'ec2': [],
 'ecr': ['EmptyUploadException', 'ImageAlreadyExistsException', 'ImageNotFoundException', 'InvalidLayerException', 'InvalidLayerPartException', 'InvalidParameterException', 'InvalidTagParameterException', 'LayerAlreadyExistsException', 'LayerInaccessibleException', 'LayerPartTooSmallException', 'LayersNotFoundException', 'LifecyclePolicyNotFoundException', 'LifecyclePolicyPreviewInProgressException', 'LifecyclePolicyPreviewNotFoundException', 'LimitExceededException', 'RepositoryAlreadyExistsException', 'RepositoryNotEmptyException', 'RepositoryNotFoundException', 'RepositoryPolicyNotFoundException', 'ServerException', 'TooManyTagsException', 'UploadNotFoundException'],
 'ecs': ['AccessDeniedException', 'AttributeLimitExceededException', 'BlockedException', 'ClientException', 'ClusterContainsContainerInstancesException', 'ClusterContainsServicesException', 'ClusterContainsTasksException', 'ClusterNotFoundException', 'InvalidParameterException', 'MissingVersionException', 'NoUpdateAvailableException', 'PlatformTaskDefinitionIncompatibilityException', 'PlatformUnknownException', 'ResourceNotFoundException', 'ServerException', 'ServiceNotActiveException', 'ServiceNotFoundException', 'TargetNotFoundException', 'UnsupportedFeatureException', 'UpdateInProgressException'],
 'efs': ['BadRequest', 'DependencyTimeout', 'FileSystemAlreadyExists', 'FileSystemInUse', 'FileSystemLimitExceeded', 'FileSystemNotFound', 'IncorrectFileSystemLifeCycleState', 'IncorrectMountTargetState', 'InsufficientThroughputCapacity', 'InternalServerError', 'IpAddressInUse', 'MountTargetConflict', 'MountTargetNotFound', 'NetworkInterfaceLimitExceeded', 'NoFreeAddressesInSubnet', 'SecurityGroupLimitExceeded', 'SecurityGroupNotFound', 'SubnetNotFound', 'ThroughputLimitExceeded', 'TooManyRequests', 'UnsupportedAvailabilityZone'],
 'eks': ['ClientException', 'InvalidParameterException', 'InvalidRequestException', 'ResourceInUseException', 'ResourceLimitExceededException', 'ResourceNotFoundException', 'ServerException', 'ServiceUnavailableException', 'UnsupportedAvailabilityZoneException'],
 'elasticache': ['APICallRateForCustomerExceeded', 'AuthorizationAlreadyExists', 'AuthorizationNotFound', 'CacheClusterAlreadyExists', 'CacheClusterNotFound', 'CacheParameterGroupAlreadyExists', 'CacheParameterGroupNotFound', 'CacheParameterGroupQuotaExceeded', 'CacheSecurityGroupAlreadyExists', 'CacheSecurityGroupNotFound', 'QuotaExceeded.CacheSecurityGroup', 'CacheSubnetGroupAlreadyExists', 'CacheSubnetGroupInUse', 'CacheSubnetGroupNotFoundFault', 'CacheSubnetGroupQuotaExceeded', 'CacheSubnetQuotaExceededFault', 'ClusterQuotaForCustomerExceeded', 'InsufficientCacheClusterCapacity', 'InvalidARN', 'InvalidCacheClusterState', 'InvalidCacheParameterGroupState', 'InvalidCacheSecurityGroupState', 'InvalidParameterCombination', 'InvalidParameterValue', 'InvalidReplicationGroupState', 'InvalidSnapshotState', 'InvalidSubnet', 'InvalidVPCNetworkStateFault', 'NoOperationFault', 'NodeGroupNotFoundFault', 'NodeGroupsPerReplicationGroupQuotaExceeded', 'NodeQuotaForClusterExceeded', 'NodeQuotaForCustomerExceeded', 'ReplicationGroupAlreadyExists', 'ReplicationGroupNotFoundFault', 'ReservedCacheNodeAlreadyExists', 'ReservedCacheNodeNotFound', 'ReservedCacheNodeQuotaExceeded', 'ReservedCacheNodesOfferingNotFound', 'ServiceLinkedRoleNotFoundFault', 'SnapshotAlreadyExistsFault', 'SnapshotFeatureNotSupportedFault', 'SnapshotNotFoundFault', 'SnapshotQuotaExceededFault', 'SubnetInUse', 'TagNotFound', 'TagQuotaPerResourceExceeded', 'TestFailoverNotAvailableFault'],
 'elasticbeanstalk': ['CodeBuildNotInServiceRegionException', 'ElasticBeanstalkServiceException', 'InsufficientPrivilegesException', 'InvalidRequestException', 'ManagedActionInvalidStateException', 'OperationInProgressFailure', 'PlatformVersionStillReferencedException', 'ResourceNotFoundException', 'ResourceTypeNotSupportedException', 'S3LocationNotInServiceRegionException', 'S3SubscriptionRequiredException', 'SourceBundleDeletionFailure', 'TooManyApplicationVersionsException', 'TooManyApplicationsException', 'TooManyBucketsException', 'TooManyConfigurationTemplatesException', 'TooManyEnvironmentsException', 'TooManyPlatformsException', 'TooManyTagsException'],
 'elb': ['LoadBalancerNotFound', 'CertificateNotFound', 'DependencyThrottle', 'DuplicateLoadBalancerName', 'DuplicateListener', 'DuplicatePolicyName', 'DuplicateTagKeys', 'InvalidConfigurationRequest', 'InvalidInstance', 'InvalidScheme', 'InvalidSecurityGroup', 'InvalidSubnet', 'ListenerNotFound', 'LoadBalancerAttributeNotFound', 'OperationNotPermitted', 'PolicyNotFound', 'PolicyTypeNotFound', 'SubnetNotFound', 'TooManyLoadBalancers', 'TooManyPolicies', 'TooManyTags', 'UnsupportedProtocol'],
 'emr': ['InternalServerError', 'InternalServerException', 'InvalidRequestException'],
 'es': ['BaseException', 'DisabledOperationException', 'InternalException', 'InvalidTypeException', 'LimitExceededException', 'ResourceAlreadyExistsException', 'ResourceNotFoundException', 'ValidationException'],
 'events': ['ConcurrentModificationException', 'InternalException', 'InvalidEventPatternException', 'LimitExceededException', 'ManagedRuleException', 'PolicyLengthExceededException', 'ResourceNotFoundException'],
 'firehose': ['ConcurrentModificationException', 'InvalidArgumentException', 'LimitExceededException', 'ResourceInUseException', 'ResourceNotFoundException', 'ServiceUnavailableException'],
 'glacier': ['InsufficientCapacityException', 'InvalidParameterValueException', 'LimitExceededException', 'MissingParameterValueException', 'PolicyEnforcedException', 'RequestTimeoutException', 'ResourceNotFoundException', 'ServiceUnavailableException'],
 'glue': ['AccessDeniedException', 'AlreadyExistsException', 'ConcurrentModificationException', 'ConcurrentRunsExceededException', 'ConditionCheckFailureException', 'CrawlerNotRunningException', 'CrawlerRunningException', 'CrawlerStoppingException', 'EntityNotFoundException', 'GlueEncryptionException', 'IdempotentParameterMismatchException', 'InternalServiceException', 'InvalidInputException', 'NoScheduleException', 'OperationTimeoutException', 'ResourceNumberLimitExceededException', 'SchedulerNotRunningException', 'SchedulerRunningException', 'SchedulerTransitioningException', 'ValidationException', 'VersionMismatchException'],
 'iam': ['ConcurrentModification', 'ReportExpired', 'ReportNotPresent', 'ReportInProgress', 'DeleteConflict', 'DuplicateCertificate', 'DuplicateSSHPublicKey', 'EntityAlreadyExists', 'EntityTemporarilyUnmodifiable', 'InvalidAuthenticationCode', 'InvalidCertificate', 'InvalidInput', 'InvalidPublicKey', 'InvalidUserType', 'KeyPairMismatch', 'LimitExceeded', 'MalformedCertificate', 'MalformedPolicyDocument', 'NoSuchEntity', 'PasswordPolicyViolation', 'PolicyEvaluation', 'PolicyNotAttachable', 'ServiceFailure', 'NotSupportedService', 'UnmodifiableEntity', 'UnrecognizedPublicKeyEncoding'],
 'kinesis': ['ExpiredIteratorException', 'ExpiredNextTokenException', 'InternalFailureException', 'InvalidArgumentException', 'KMSAccessDeniedException', 'KMSDisabledException', 'KMSInvalidStateException', 'KMSNotFoundException', 'KMSOptInRequired', 'KMSThrottlingException', 'LimitExceededException', 'ProvisionedThroughputExceededException', 'ResourceInUseException', 'ResourceNotFoundException'],
 'kms': ['AlreadyExistsException', 'CloudHsmClusterInUseException', 'CloudHsmClusterInvalidConfigurationException', 'CloudHsmClusterNotActiveException', 'CloudHsmClusterNotFoundException', 'CloudHsmClusterNotRelatedException', 'CustomKeyStoreHasCMKsException', 'CustomKeyStoreInvalidStateException', 'CustomKeyStoreNameInUseException', 'CustomKeyStoreNotFoundException', 'DependencyTimeoutException', 'DisabledException', 'ExpiredImportTokenException', 'IncorrectKeyMaterialException', 'IncorrectTrustAnchorException', 'InvalidAliasNameException', 'InvalidArnException', 'InvalidCiphertextException', 'InvalidGrantIdException', 'InvalidGrantTokenException', 'InvalidImportTokenException', 'InvalidKeyUsageException', 'InvalidMarkerException', 'KMSInternalException', 'KMSInvalidStateException', 'KeyUnavailableException', 'LimitExceededException', 'MalformedPolicyDocumentException', 'NotFoundException', 'TagException', 'UnsupportedOperationException'],
 'lambda': ['CodeStorageExceededException', 'EC2AccessDeniedException', 'EC2ThrottledException', 'EC2UnexpectedException', 'ENILimitReachedException', 'InvalidParameterValueException', 'InvalidRequestContentException', 'InvalidRuntimeException', 'InvalidSecurityGroupIDException', 'InvalidSubnetIDException', 'InvalidZipFileException', 'KMSAccessDeniedException', 'KMSDisabledException', 'KMSInvalidStateException', 'KMSNotFoundException', 'PolicyLengthExceededException', 'PreconditionFailedException', 'RequestTooLargeException', 'ResourceConflictException', 'ResourceInUseException', 'ResourceNotFoundException', 'ServiceException', 'SubnetIPAddressLimitReachedException', 'TooManyRequestsException', 'UnsupportedMediaTypeException'],
 'logs': ['DataAlreadyAcceptedException', 'InvalidOperationException', 'InvalidParameterException', 'InvalidSequenceTokenException', 'LimitExceededException', 'MalformedQueryException', 'OperationAbortedException', 'ResourceAlreadyExistsException', 'ResourceNotFoundException', 'ServiceUnavailableException', 'UnrecognizedClientException'],
 'neptune': ['AuthorizationNotFound', 'CertificateNotFound', 'DBClusterAlreadyExistsFault', 'DBClusterNotFoundFault', 'DBClusterParameterGroupNotFound', 'DBClusterQuotaExceededFault', 'DBClusterRoleAlreadyExists', 'DBClusterRoleNotFound', 'DBClusterRoleQuotaExceeded', 'DBClusterSnapshotAlreadyExistsFault', 'DBClusterSnapshotNotFoundFault', 'DBInstanceAlreadyExists', 'DBInstanceNotFound', 'DBParameterGroupAlreadyExists', 'DBParameterGroupNotFound', 'DBParameterGroupQuotaExceeded', 'DBSecurityGroupNotFound', 'DBSnapshotAlreadyExists', 'DBSnapshotNotFound', 'DBSubnetGroupAlreadyExists', 'DBSubnetGroupDoesNotCoverEnoughAZs', 'DBSubnetGroupNotFoundFault', 'DBSubnetGroupQuotaExceeded', 'DBSubnetQuotaExceededFault', 'DBUpgradeDependencyFailure', 'DomainNotFoundFault', 'EventSubscriptionQuotaExceeded', 'InstanceQuotaExceeded', 'InsufficientDBClusterCapacityFault', 'InsufficientDBInstanceCapacity', 'InsufficientStorageClusterCapacity', 'InvalidDBClusterSnapshotStateFault', 'InvalidDBClusterStateFault', 'InvalidDBInstanceState', 'InvalidDBParameterGroupState', 'InvalidDBSecurityGroupState', 'InvalidDBSnapshotState', 'InvalidDBSubnetGroupStateFault', 'InvalidDBSubnetStateFault', 'InvalidEventSubscriptionState', 'InvalidRestoreFault', 'InvalidSubnet', 'InvalidVPCNetworkStateFault', 'KMSKeyNotAccessibleFault', 'OptionGroupNotFoundFault', 'ProvisionedIopsNotAvailableInAZFault', 'ResourceNotFoundFault', 'SNSInvalidTopic', 'SNSNoAuthorization', 'SNSTopicArnNotFound', 'SharedSnapshotQuotaExceeded', 'SnapshotQuotaExceeded', 'SourceNotFound', 'StorageQuotaExceeded', 'StorageTypeNotSupported', 'SubnetAlreadyInUse', 'SubscriptionAlreadyExist', 'SubscriptionCategoryNotFound', 'SubscriptionNotFound'],
 'rds': ['AuthorizationAlreadyExists', 'AuthorizationNotFound', 'AuthorizationQuotaExceeded', 'BackupPolicyNotFoundFault', 'CertificateNotFound', 'DBClusterAlreadyExistsFault', 'DBClusterBacktrackNotFoundFault', 'DBClusterEndpointAlreadyExistsFault', 'DBClusterEndpointNotFoundFault', 'DBClusterEndpointQuotaExceededFault', 'DBClusterNotFoundFault', 'DBClusterParameterGroupNotFound', 'DBClusterQuotaExceededFault', 'DBClusterRoleAlreadyExists', 'DBClusterRoleNotFound', 'DBClusterRoleQuotaExceeded', 'DBClusterSnapshotAlreadyExistsFault', 'DBClusterSnapshotNotFoundFault', 'DBInstanceAlreadyExists', 'DBInstanceAutomatedBackupNotFound', 'DBInstanceAutomatedBackupQuotaExceeded', 'DBInstanceNotFound', 'DBInstanceRoleAlreadyExists', 'DBInstanceRoleNotFound', 'DBInstanceRoleQuotaExceeded', 'DBLogFileNotFoundFault', 'DBParameterGroupAlreadyExists', 'DBParameterGroupNotFound', 'DBParameterGroupQuotaExceeded', 'DBSecurityGroupAlreadyExists', 'DBSecurityGroupNotFound', 'DBSecurityGroupNotSupported', 'QuotaExceeded.DBSecurityGroup', 'DBSnapshotAlreadyExists', 'DBSnapshotNotFound', 'DBSubnetGroupAlreadyExists', 'DBSubnetGroupDoesNotCoverEnoughAZs', 'DBSubnetGroupNotAllowedFault', 'DBSubnetGroupNotFoundFault', 'DBSubnetGroupQuotaExceeded', 'DBSubnetQuotaExceededFault', 'DBUpgradeDependencyFailure', 'DomainNotFoundFault', 'EventSubscriptionQuotaExceeded', 'GlobalClusterAlreadyExistsFault', 'GlobalClusterNotFoundFault', 'GlobalClusterQuotaExceededFault', 'InstanceQuotaExceeded', 'InsufficientDBClusterCapacityFault', 'InsufficientDBInstanceCapacity', 'InsufficientStorageClusterCapacity', 'InvalidDBClusterCapacityFault', 'InvalidDBClusterEndpointStateFault', 'InvalidDBClusterSnapshotStateFault', 'InvalidDBClusterStateFault', 'InvalidDBInstanceAutomatedBackupState', 'InvalidDBInstanceState', 'InvalidDBParameterGroupState', 'InvalidDBSecurityGroupState', 'InvalidDBSnapshotState', 'InvalidDBSubnetGroupFault', 'InvalidDBSubnetGroupStateFault', 'InvalidDBSubnetStateFault', 'InvalidEventSubscriptionState', 'InvalidGlobalClusterStateFault', 'InvalidOptionGroupStateFault', 'InvalidRestoreFault', 'InvalidS3BucketFault', 'InvalidSubnet', 'InvalidVPCNetworkStateFault', 'KMSKeyNotAccessibleFault', 'OptionGroupAlreadyExistsFault', 'OptionGroupNotFoundFault', 'OptionGroupQuotaExceededFault', 'PointInTimeRestoreNotEnabled', 'ProvisionedIopsNotAvailableInAZFault', 'ReservedDBInstanceAlreadyExists', 'ReservedDBInstanceNotFound', 'ReservedDBInstanceQuotaExceeded', 'ReservedDBInstancesOfferingNotFound', 'ResourceNotFoundFault', 'SNSInvalidTopic', 'SNSNoAuthorization', 'SNSTopicArnNotFound', 'SharedSnapshotQuotaExceeded', 'SnapshotQuotaExceeded', 'SourceNotFound', 'StorageQuotaExceeded', 'StorageTypeNotSupported', 'SubnetAlreadyInUse', 'SubscriptionAlreadyExist', 'SubscriptionCategoryNotFound', 'SubscriptionNotFound'],
 'route53': ['ConcurrentModification', 'ConflictingDomainExists', 'ConflictingTypes', 'DelegationSetAlreadyCreated', 'DelegationSetAlreadyReusable', 'DelegationSetInUse', 'DelegationSetNotAvailable', 'DelegationSetNotReusable', 'HealthCheckAlreadyExists', 'HealthCheckInUse', 'HealthCheckVersionMismatch', 'HostedZoneAlreadyExists', 'HostedZoneNotEmpty', 'HostedZoneNotFound', 'HostedZoneNotPrivate', 'IncompatibleVersion', 'InsufficientCloudWatchLogsResourcePolicy', 'InvalidArgument', 'InvalidChangeBatch', 'InvalidDomainName', 'InvalidInput', 'InvalidPaginationToken', 'InvalidTrafficPolicyDocument', 'InvalidVPCId', 'LastVPCAssociation', 'LimitsExceeded', 'NoSuchChange', 'NoSuchCloudWatchLogsLogGroup', 'NoSuchDelegationSet', 'NoSuchGeoLocation', 'NoSuchHealthCheck', 'NoSuchHostedZone', 'NoSuchQueryLoggingConfig', 'NoSuchTrafficPolicy', 'NoSuchTrafficPolicyInstance', 'NotAuthorizedException', 'PriorRequestNotComplete', 'PublicZoneVPCAssociation', 'QueryLoggingConfigAlreadyExists', 'ThrottlingException', 'TooManyHealthChecks', 'TooManyHostedZones', 'TooManyTrafficPolicies', 'TooManyTrafficPolicyInstances', 'TooManyTrafficPolicyVersionsForCurrentPolicy', 'TooManyVPCAssociationAuthorizations', 'TrafficPolicyAlreadyExists', 'TrafficPolicyInUse', 'TrafficPolicyInstanceAlreadyExists', 'VPCAssociationAuthorizationNotFound', 'VPCAssociationNotFound'],
 's3': ['BucketAlreadyExists', 'BucketAlreadyOwnedByYou', 'NoSuchBucket', 'NoSuchKey', 'NoSuchUpload', 'ObjectAlreadyInActiveTierError', 'ObjectNotInActiveTierError'],
 'sagemaker': ['ResourceInUse', 'ResourceLimitExceeded', 'ResourceNotFound'],
 'secretsmanager': ['DecryptionFailure', 'EncryptionFailure', 'InternalServiceError', 'InvalidNextTokenException', 'InvalidParameterException', 'InvalidRequestException', 'LimitExceededException', 'MalformedPolicyDocumentException', 'PreconditionNotMetException', 'ResourceExistsException', 'ResourceNotFoundException'],
 'ses': ['AccountSendingPausedException', 'AlreadyExists', 'CannotDelete', 'ConfigurationSetAlreadyExists', 'ConfigurationSetDoesNotExist', 'ConfigurationSetSendingPausedException', 'CustomVerificationEmailInvalidContent', 'CustomVerificationEmailTemplateAlreadyExists', 'CustomVerificationEmailTemplateDoesNotExist', 'EventDestinationAlreadyExists', 'EventDestinationDoesNotExist', 'FromEmailAddressNotVerified', 'InvalidCloudWatchDestination', 'InvalidConfigurationSet', 'InvalidFirehoseDestination', 'InvalidLambdaFunction', 'InvalidPolicy', 'InvalidRenderingParameter', 'InvalidS3Configuration', 'InvalidSNSDestination', 'InvalidSnsTopic', 'InvalidTemplate', 'InvalidTrackingOptions', 'LimitExceeded', 'MailFromDomainNotVerifiedException', 'MessageRejected', 'MissingRenderingAttribute', 'ProductionAccessNotGranted', 'RuleDoesNotExist', 'RuleSetDoesNotExist', 'TemplateDoesNotExist', 'TrackingOptionsAlreadyExistsException', 'TrackingOptionsDoesNotExistException'],
 'sns': ['AuthorizationError', 'EndpointDisabled', 'FilterPolicyLimitExceeded', 'InternalError', 'InvalidParameter', 'ParameterValueInvalid', 'InvalidSecurity', 'KMSAccessDenied', 'KMSDisabled', 'KMSInvalidState', 'KMSNotFound', 'KMSOptInRequired', 'KMSThrottling', 'NotFound', 'PlatformApplicationDisabled', 'SubscriptionLimitExceeded', 'Throttled', 'TopicLimitExceeded'],
 'sqs': ['AWS.SimpleQueueService.BatchEntryIdsNotDistinct', 'AWS.SimpleQueueService.BatchRequestTooLong', 'AWS.SimpleQueueService.EmptyBatchRequest', 'InvalidAttributeName', 'AWS.SimpleQueueService.InvalidBatchEntryId', 'InvalidIdFormat', 'InvalidMessageContents', 'AWS.SimpleQueueService.MessageNotInflight', 'OverLimit', 'AWS.SimpleQueueService.PurgeQueueInProgress', 'AWS.SimpleQueueService.QueueDeletedRecently', 'AWS.SimpleQueueService.NonExistentQueue', 'QueueAlreadyExists', 'ReceiptHandleIsInvalid', 'AWS.SimpleQueueService.TooManyEntriesInBatchRequest', 'AWS.SimpleQueueService.UnsupportedOperation'],
 'ssm': ['AlreadyExistsException', 'AssociatedInstances', 'AssociationAlreadyExists', 'AssociationDoesNotExist', 'AssociationExecutionDoesNotExist', 'AssociationLimitExceeded', 'AssociationVersionLimitExceeded', 'AutomationDefinitionNotFoundException', 'AutomationDefinitionVersionNotFoundException', 'AutomationExecutionLimitExceededException', 'AutomationExecutionNotFoundException', 'AutomationStepNotFoundException', 'ComplianceTypeCountLimitExceededException', 'CustomSchemaCountLimitExceededException', 'DocumentAlreadyExists', 'DocumentLimitExceeded', 'DocumentPermissionLimit', 'DocumentVersionLimitExceeded', 'DoesNotExistException', 'DuplicateDocumentContent', 'DuplicateDocumentVersionName', 'DuplicateInstanceId', 'FeatureNotAvailableException', 'HierarchyLevelLimitExceededException', 'HierarchyTypeMismatchException', 'IdempotentParameterMismatch', 'InternalServerError', 'InvalidActivation', 'InvalidActivationId', 'InvalidAggregatorException', 'InvalidAllowedPatternException', 'InvalidAssociation', 'InvalidAssociationVersion', 'InvalidAutomationExecutionParametersException', 'InvalidAutomationSignalException', 'InvalidAutomationStatusUpdateException', 'InvalidCommandId', 'InvalidDeleteInventoryParametersException', 'InvalidDeletionIdException', 'InvalidDocument', 'InvalidDocumentContent', 'InvalidDocumentOperation', 'InvalidDocumentSchemaVersion', 'InvalidDocumentVersion', 'InvalidFilter', 'InvalidFilterKey', 'InvalidFilterOption', 'InvalidFilterValue', 'InvalidInstanceId', 'InvalidInstanceInformationFilterValue', 'InvalidInventoryGroupException', 'InvalidInventoryItemContextException', 'InvalidInventoryRequestException', 'InvalidItemContentException', 'InvalidKeyId', 'InvalidNextToken', 'InvalidNotificationConfig', 'InvalidOptionException', 'InvalidOutputFolder', 'InvalidOutputLocation', 'InvalidParameters', 'InvalidPermissionType', 'InvalidPluginName', 'InvalidResourceId', 'InvalidResourceType', 'InvalidResultAttributeException', 'InvalidRole', 'InvalidSchedule', 'InvalidTarget', 'InvalidTypeNameException', 'InvalidUpdate', 'InvocationDoesNotExist', 'ItemContentMismatchException', 'ItemSizeLimitExceededException', 'MaxDocumentSizeExceeded', 'ParameterAlreadyExists', 'ParameterLimitExceeded', 'ParameterMaxVersionLimitExceeded', 'ParameterNotFound', 'ParameterPatternMismatchException', 'ParameterVersionLabelLimitExceeded', 'ParameterVersionNotFound', 'ResourceDataSyncAlreadyExistsException', 'ResourceDataSyncCountExceededException', 'ResourceDataSyncInvalidConfigurationException', 'ResourceDataSyncNotFoundException', 'ResourceInUseException', 'ResourceLimitExceededException', 'StatusUnchanged', 'SubTypeCountLimitExceededException', 'TargetInUseException', 'TargetNotConnected', 'TooManyTagsError', 'TooManyUpdates', 'TotalSizeLimitExceededException', 'UnsupportedInventoryItemContextException', 'UnsupportedInventorySchemaVersionException', 'UnsupportedOperatingSystem', 'UnsupportedParameterType', 'UnsupportedPlatformType'],
 'stepfunctions': ['ActivityDoesNotExist', 'ActivityLimitExceeded', 'ActivityWorkerLimitExceeded', 'ExecutionAlreadyExists', 'ExecutionDoesNotExist', 'ExecutionLimitExceeded', 'InvalidArn', 'InvalidDefinition', 'InvalidExecutionInput', 'InvalidName', 'InvalidOutput', 'InvalidToken', 'MissingRequiredParameter', 'ResourceNotFound', 'StateMachineAlreadyExists', 'StateMachineDeleting', 'StateMachineDoesNotExist', 'StateMachineLimitExceeded', 'TaskDoesNotExist', 'TaskTimedOut', 'TooManyTags'],
 'sts': ['ExpiredTokenException', 'IDPCommunicationError', 'IDPRejectedClaim', 'InvalidAuthorizationMessageException', 'InvalidIdentityToken', 'MalformedPolicyDocument', 'PackedPolicyTooLarge', 'RegionDisabledException'],
 'xray': ['InvalidRequestException', 'RuleLimitExceededException', 'ThrottledException']}

It would be nice if ExpiredTokenException were a named exception, or if I knew how to generate a class based on it:

-> LOG.exception("ssm:GetParameter(Name='/standard/pAwsAccount')")
(Pdb) p e
ClientError('An error occurred (ExpiredTokenException) when calling the GetParameter operation: The security token included in the request is expired',)
(Pdb) dir(e)
['MSG_TEMPLATE', '__cause__', '__class__', '__context__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__suppress_context__', '__traceback__', '__weakref__', '_get_retry_info', 'args', 'operation_name', 'response', 'with_traceback']
(Pdb) p e.operation_name
'GetParameter'
(Pdb) p e.response['Error']['Code']
'ExpiredTokenException'
(Pdb) e.__class__.__name__
'ClientError'

Imagine you have a call that may fail (known more commonly as «every call»). It’s a well-documented fact that saying «hah, no need to worry about X failing» is the only way to guarantee X will fail and page you at 3 am. You might write code like this using the S3 client to create a new bucket.

>>> import boto3
>>> s3 = boto3.client('s3')
>>> s3.create_bucket(Bucket='test', ACL='private')
botocore.exceptions.ClientError: An error occurred (IllegalLocationConstraintException) when calling the CreateBucket operation: The unspecified location constraint is incompatible for the region specific endpoint this request was sent to. 

This is one of the more common exceptions: a botocore ClientError is bubbling up from the API call layer (botocore) up to your higher-level call (boto3). Unfortunately, the type ClientError doesn’t give us enough information to be useful. You can import botocore.exceptions and handle this error by the class (ClientError in this case) but that information isn’t enough.

We have to do more work handle errors based on their error code like these from the S3 docs. Your boto3 client also carries its own exception factory — you can build an exception class from any error code from those docs and catch it.

import boto3
s3 = boto3.client('s3')
try:
    s3.create_bucket(Bucket='test')
except s3.exceptions.from_code('IllegalLocationConstraintException'):
    print('bad location')

Each of these error codes might need special handling. The IllegalLocationConstraintException error above isn’t something we can retry our way out of. The error is happening because S3 buckets are globally unique and we’re trying to make one called test which someone else has definitely used. We need to pick a new bucket name for the call to succeed. If we were hitting the API too frequently we would get a ClientError with a SlowDown code telling us to wait a little bit before continuing. This would happen if we’re bumping up against the S3 API rate limit.

But this doesn’t work how we think it does. If you try to catch two different error codes with this method we will always enter the first handler like this:

import boto3
s3 = boto3.client('s3')
try:
    s3.create_bucket(Bucket='test')
except ddb.exceptions.from_code('SlowDown'):
    print('Calm yourself')
except ddb.exceptions.from_code('IllegalLocationConstraintException'):
    print('Bad location')

No matter what ClientError we hit this code will always print «Calm yourself» because Python will match the first except that has a matching exception. This is why you want to handle exceptions from most specific to least. Since we can’t handle it based on the type of the exception, we need to use the e.response['Error']['Code'] attribute to distinguish between error codes and take the right action.

import boto3
import botocore.exceptions
s3 = boto3.client('s3')
try:
    s3.create_bucket(Bucket='test')
except botocore.exceptions.ClientError as e:
    if e.response['Error']['Code'] == 'SlowDown':
        print('Calm yourself')
    if e.response['Error']['Code'] == 'IllegalLocationConstraintException':
        print('Bad location')
    else: # neither of those codes were right, so we re-raise the exception
        raise

Now we’ve handled our error, but there may be a better way for us to deal with the wide variety of codes we might want to handle. One little-known fact about Python is the except clause is executed when the exception is being handled and doesn’t just have to be an exception class. That’s why we were able to write this line in an earlier example:

except ddb.exceptions.from_code('IllegalLocationConstraintException'):

The from_code function returns an exception class for Python to check and either catch or pass on. We can take advantage of this to examine the current exception. Then we can check the error code and if it’s the one we’re trying to match we return a ClientError that will trigger the clause to handle our exception. If we don’t find a match, we’ll return an exception subclass that doesn’t exist (the NeverEverRaisedException) so that the clause will never be run.

import sys
import boto3
from botocore.exceptions import ClientError

def is_client_error(code):
    e = sys.exc_info()[1]
    if isinstance(e, ClientError) and e.response["Error"]["Code"] == code:
        return ClientError
    return type("NeverEverRaisedException", (Exception,), {})

s3 = boto3.client('s3')
try:
    s3.create_bucket(Bucket='test')
except is_client_error('SlowDown'):
    print('Calm yourself')
except is_client_error('IllegalLocationConstraintException'):
    print('Bad location')

Now when we run our snippet we get the output we expect: Bad location because the SlowDown clause never matched. But there’s still one more thing we can do. If we’re outside of an exception, we still want to be able to easily check error codes in a truthy/falsy way. To do that we add a None-defaulting keyword argument so we can use our function like this too:

if is_client_error('SlowDown', exc):
    time.sleep(1)
    pass

The Final Handler

This is useful when you’re being passed in an exception but are outside the exception handler itself such as bubbling up the exception to a logging library for enrichment. Our final function looks like this:

import sys
from botocore.exceptions import ClientError

def is_client_error(code, e=None):
    """Match a botocore.exceptions.ClientError to an error code.

    Returns ClientError if the error code matches, else a dummy exception.

    Based on Ansible's GPL `is_boto3_error_code` https://github.com/ansible/ansible/blob/stable-2.9/lib/ansible/module_utils/aws/core.py
    """
    if e is None:
        exc = sys.exc_info()[1]
    else:
        exc = e
    if isinstance(exc, ClientError) and exc.response["Error"]["Code"] == code:
        return ClientError
    return type("NeverEverRaisedException", (Exception,), {}) if e is None else False

We can handle being called as part of an except clause, or anywhere else we find a ClientError to match. This should help you write more readable code and handle errors when working with AWS APIs better.

Thanks for reading, and have a pythonic day.

I am developing a django app which communicates with several Amazon Web Services.

So far I am having trouble dealing with and catching exceptions thrown by the boto3 client. What I am doing seems unnecessarily tedious:

Example:

client = boto3.client('sns')
client.create_platform_endpoint(PlatformApplicationArn=SNS_APP_ARN, Token=token)

this might throw an botocore.errorfactory.InvalidParameterException if e.g. the token is bad.

client.get_endpoint_attributes(EndpointArn=endpoint_arn)

might throw an botocore.errorfactory.NotFoundException.

First, I can’t find these Errors anywhere in code, so they are probably generated somewhere. Bottom line: I can’t import it and catch it as usual.

Second, I found one way to catch the error here using:

try:
    # boto3 stuff
except botocore.exceptions.ClientError as e:
    if e.response['Error']['Code'] == 'NotFound':
        # handle exception
    else:
        raise e

But I have to remove the Exception part of the error name. Seems very random and I have no clue whether I would remove the Error in botocore.exceptions.ParamValidationError if I wanted to catch that one. So it’s hard to generalize.

Another way to catch the error is using the boto3 client object I got:

try:
    # boto3 stuff
except client.exceptions.NotFoundException as e:
    # handle exception

This seems the cleanest way so far. But I don’t always have the boto3 client object at hand where I want to catch the error. Also I am still only trying things out, so it’s mostly guess work.

Does anybody know how boto3 errors are supposed to be handled?

Or can point me towards some coherent documentation which mentions the errors above? Thanks

Making botocore.exceptions.ClientError easier to deal with

All AWS service exceptions are raised by boto3 as a botocore.exceptions.ClientError, with the contents of the exception indicating what kind of exception happened.
This is not very pythonic, and the contents themselves are rather opaque, most being held in dicts rather than as properties.
The functions in this package help dealing with that, to make your code less verbose and require less memorization of ClientError contents.

Installation

The package is on PyPI for pip-installing, but you can also just copy the aws_error_utils.py file into your project (note that the standalone file requires Python 3.7 or greater).

Usage

If you’ve got code like this:

import boto3, botocore.exceptions

s3 = boto3.client('s3')
try:
    s3.get_object(Bucket='my-bucket', Key='example')
except botocore.exceptions.ClientError as error:
    if error.response['Error']['Code'] == 'NoSuchBucket':
        print(error.response['Error']['Message'])
        # error handling
    else:
        raise

you can replace it with:

import boto3
from aws_error_utils import errors

s3 = boto3.client('s3')
try:
    s3.get_object(Bucket='my-bucket', Key='example')
except errors.NoSuchBucket as error:
# or
# except catch_aws_error('NoSuchBucket') as error:
    print(error.message)
    # error handling

If you have trouble remembering where all the contents in ClientError are, like these:

client_error.response['Error']['Code']
client_error.response['Error']['Message']
client_error.response['ResponseMetadata']['HTTPStatusCode']
client_error.operation_name

you can replace it with:

import boto3
from aws_error_utils import get_aws_error_info

err_info = get_aws_error_info(client_error)

err_info.code
err_info.message
err_info.http_status_code
err_info.operation_name

If you’re using errors or catch_aws_error(), you can skip the get_aws_error_info() step, because the fields are set directly on the ClientError object:

import boto3
from aws_error_utils import errors

s3 = boto3.client('s3')
try:
    s3.get_object(Bucket='my-bucket', Key='example')
except errors.NoSuchBucket as error:
    error.code
    error.message
    error.http_status_code
    error.operation_name

Additionally, when you do need to use ClientError, it’s imported by aws_error_utils so you can just use aws_error_utils.ClientError rather than botocore.exceptions.ClientError (the same is true for BotoCoreError, the base class of all non-ClientError exceptions).

errors

It’s easiest to use the errors class if you don’t have complex conditions to match.
Using the error code as a field name in an except block will match that error code.
Additionally, when you use this style, it sets the fields from AWSErrorInfo (see below) directly on the ClientError object.
For example:

import boto3
from aws_error_utils import errors

s3 = boto3.client('s3')
try:
    s3.get_object(Bucket='my-bucket', Key='example')
except errors.NoSuchBucket as error:
    print(error.message)

    # error handling

You can include multiple error codes in an except statement, though this is slower than combining them with a single catch_aws_error() call.

import boto3
from aws_error_utils import errors

s3 = boto3.client('s3')
try:
    s3.get_object(Bucket='my-bucket', Key='example')
except (errors.NoSuchBucket, errors.NoSuchKey) as error:
    print(error.message)

    # error handling

You can catch all ClientErrors with errors.ALL.

You can only use this style for error codes that work as Python property names.
For error codes like EC2’s InvalidInstanceID.NotFound, you have to use catch_aws_error() (see below).

Unfortunately, you cannot get tab completion for error codes on the errors class, as a comprehensive list of error codes is not available as a Python package (botocore has a small number, but they are few and incomplete).

Note that the value of errors.NoSuchBucket is not an exception class representing the NoSuchBucket error, it is an alias for catch_aws_error('NoSuchBucket').
It can only be used in an except statement; it will raise RuntimeError otherwise.
You also cannot instantiate the errors class.

catch_aws_error()

The function takes as input error code(s), and optionally operation name(s), to match against the current raised exception. If the exception matches, the except block is executed.
If your error handling still needs the error object, you can still use an as expression, otherwise it can be omitted (just except catch_aws_error(...):).
Additionally, catch_aws_error() sets the fields from AWSErrorInfo (see below) directly on the ClientError object.

import boto3
from aws_error_utils import catch_aws_error

s3 = boto3.client('s3')
try:
    s3.get_object(Bucket='my-bucket', Key='example')
except catch_aws_error('NoSuchBucket') as error:
    print(error.message)

    # error handling

You can provide error codes either as positional args, or as the code keyword argument with either as a single string or a list of strings.

catch_aws_error('NoSuchBucket')
catch_aws_error(code='NoSuchBucket')

catch_aws_error('NoSuchBucket', 'NoSuchKey')
catch_aws_error(code=['NoSuchBucket', 'NoSuchKey'])

If there are multiple API calls in the try block, and you want to match against specific ones, the operation_name keyword argument can help.
Similar to the code keyword argument, the operation name(s) can be provided as either as a single string or a list of strings.

import boto3
from aws_error_utils import catch_aws_error

try:
    s3 = boto3.client('s3')
    s3.list_objects_v2(Bucket='bucket-1')
    s3.get_object(Bucket='bucket-2', Key='example')
except catch_aws_error('NoSuchBucket', operation_name='GetObject') as error:
    # This will be executed if the GetObject operation raises NoSuchBucket
    # but not if the ListObjects operation raises it

You must provide an error code.
To match exclusively against operation name, use the aws_error_utils.ALL_CODES token.
For completeness, there is also an ALL_OPERATIONS token.

import boto3
from aws_error_utils import catch_aws_error

try:
    s3 = boto3.client('s3')
    s3.list_objects_v2(Bucket='bucket-1')
    s3.get_object(Bucket='bucket-1', Key='example')
except catch_aws_error(ALL_CODES, operation_name='ListObjectsV2') as e:
    # This will execute for all ClientError exceptions raised by the ListObjectsV2 call

For more complex conditions, instead of providing error codes and operation names, you can provide a callable to evaluate the exception.
Note that unlike error codes, you can only provide a single callable.

import re
import boto3
from aws_error_utils import catch_aws_error, get_aws_error_info

def matcher(e):
    info = get_aws_error_info(e)
    return re.search('does not exist', info.message)

try:
    s3 = boto3.client('s3')
    s3.list_objects_v2(Bucket='bucket-1')
except catch_aws_error(matcher) as e:
    # This will be executed if e is a ClientError and matcher(e) returns True
    # Note the callable can assume the exception is a ClientError

get_aws_error_info()

This function takes a returns an AWSErrorInfo object, which is a collections.NamedTuple with the following fields:

  • code
  • message
  • http_status_code
  • operation_name
  • response (the raw response dictionary)

If you’re not modifying your except statements to use catch_aws_error(), this function can be useful instead of remembering exactly how this information is stored in the ClientError object.

If you’re using catch_aws_error(), this function isn’t necessary, because it sets these fields directly on the ClientError object.

aws_error_matches()

This is the matching logic behind catch_aws_error().
It takes a ClientError, with the rest of the arguments being error codes and operation names identical to catch_aws_error(), except that it does not support providing a callable.

make_aws_error()

For testing, you can create ClientErrors with the make_aws_error() function, which takes the following arguments:

  • code (required)
  • message (required)
  • operation_name (required)
  • http_status_code
  • response

If a response dict is provided, it will be copied rather than modified.

How do I resolve «unknown service», «parameter validation failed», and «object has no attribute» errors from a Python (Boto 3) Lambda function?

Last updated: 2021-11-29

My Python (Boto 3) AWS Lambda function returns «unknown service», «parameter validation failed», or «object has no attribute» errors. Why is my function returning these errors, and how do I resolve the issue?

Short description

A Python (Boto 3) Lambda function that isn’t using the latest version of Boto 3 can return any of the following errors:

  • unknown service
  • parameter validation failed
  • object has no attribute

These errors occur when the function tries to call an AWS service or AWS API that requires the latest version of Boto 3.

To resolve the issue, create a Lambda layer that uses the latest version of Boto 3. Then, add the layer to your function’s configuration.

Important: The following procedures assumes that you’re using the latest version of Botocore. If you’re not using the latest version of Botocore, you must upgrade Botocore before you can upgrade to the latest Boto 3 version. Adjust the steps that you take as needed for your specific operating system and Python configuration.

Resolution

Note: If you receive errors when running AWS Command Line Interface (AWS CLI) commands, make sure that you’re using the most recent AWS CLI version.

It’s a best practice to create a Lambda layer on the same operating system that your Lambda runtime is based on. For example, Python 3.8 is based on an Amazon Linux 2 Amazon Machine Image (AMI). However, Python 3.7 and Python 3.6 are based on the Amazon Linux AMI.

(Prerequisites) Install pip3 and the latest AWS CLI version

Create a Lambda layer that uses the latest Boto 3 version

Important: The following AWS CLI commands work for Linux, Unix, and macOS operating systems. In each command, make sure that you replace boto3-mylayer with your preferred name for the lib folder and Lambda layer.

1.    Create a lib folder by running the following command:

LIB_DIR=boto3-mylayer/python
mkdir -p $LIB_DIR

2.    Install the library to LIB_DIR by running the following command:

pip3 install boto3 -t $LIB_DIR

3.    Zip all the dependencies to /tmp/boto3-mylayer.zip by running the following command:

cd boto3-mylayer
zip -r /tmp/boto3-mylayer.zip .

4.    Publish the layer by running the following command:

aws lambda publish-layer-version --layer-name boto3-mylayer --zip-file fileb:///tmp/boto3-mylayer.zip

arn:aws:lambda:region:$ACC_ID:layer:boto3-mylayer:1

Add the new layer to your Lambda function’s configuration

To add the new layer to your Lambda function’s configuration, run the following command:

Important: Replace myfunction with your function’s name. Replace <layer ARN> with your layer’s ARN.

aws lambda update-function-configuration --function-name myfunction --layers <layer ARN>

All AWS services and arguments are now available to your Lambda function.

Tip: Use print(boto3.__version__) and print(botocore.__version__) in your function code to confirm the version of Boto 3 and Botocore.


Did this article help?


Do you need billing or technical support?

AWS support for Internet Explorer ends on 07/31/2022. Supported browsers are Chrome, Firefox, Edge, and Safari.
Learn more »

Понравилась статья? Поделить с друзьями:
  • Bosch стиральная машина ошибка e18 что это значит
  • Bosch стиралка ошибка e17
  • Bosch сброс ошибки f63
  • Bosch посудомойка ошибка е23
  • Bosch посудомойка ошибка е17