Handling errors: ClientError and friends

boto3 · Essentials · 10 min

Correct error handling separates 'not found' from 'access denied' from 'network down', each needing a different response.

Errors returned by an AWS service raise `botocore.exceptions.ClientError`. The details are in e.response:

  • e.response["Error"]["Code"]: the AWS error code, like NoSuchKey, AccessDenied, ThrottlingException, ConditionalCheckFailedException.
  • e.response["Error"]["Message"] and e.response["ResponseMetadata"]["HTTPStatusCode"].
  • The request id in ResponseMetadata, which AWS support asks for.

Many services also expose modeled exceptions as attributes of the client: except s3.exceptions.NoSuchKey: or except ddb.exceptions.ConditionalCheckFailedException:. They're subclasses of ClientError, so .response still works. Not every error is modeled: head_object on a missing key raises a plain ClientError with code "404", because HEAD responses have no body to carry an error code.

Problems on the client side raise subclasses of `BotoCoreError` instead: NoCredentialsError, NoRegionError, EndpointConnectionError, ReadTimeoutError, ParamValidationError (wrong parameter names or types, caught before any request is sent).

Guidelines:

  • Catch specific codes you can handle, re-raise everything else.
  • AccessDenied usually means an IAM policy problem: don't retry it.
  • Throttling and 5xx errors are retried automatically by botocore's retry handler; tune that rather than writing retry loops (see the configuration lesson).

Example

import boto3
from botocore.exceptions import ClientError, ParamValidationError

s3 = boto3.client("s3", region_name="us-east-1")
s3.create_bucket(Bucket="demo-errors-2026")

def read_config(key):
    try:
        return s3.get_object(Bucket="demo-errors-2026", Key=key)["Body"].read()
    except s3.exceptions.NoSuchKey:
        return b"{}"                                  # sensible default

print(read_config("missing.json"))                    # b'{}'

try:
    s3.head_object(Bucket="demo-errors-2026", Key="missing.json")
except ClientError as e:
    print(e.response["Error"]["Code"])                 # 404

try:
    s3.get_object(Bucket="demo-errors-2026")          # Key is required
except ParamValidationError as e:
    print("bad params:", type(e).__name__)             # bad params: ParamValidationError

Remember

  • Service errors raise ClientError; the code is in e.response['Error']['Code']
  • client.exceptions.X are modeled ClientError subclasses
  • head_object reports missing keys as code '404'
  • BotoCoreError subclasses cover credentials, network and parameter problems

Watch out for

  • Catching ClientError broadly and treating AccessDenied like 'not found'.

Check yourself

  1. How do you get the AWS error code from a ClientError e?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: boto3.amazonaws.com/v1/documentation/api/latest/guide/error-handling.html