boto3
error handling
AWS SDK
Python
cloud computing

How to handle errors with boto3?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Good Boto3 error handling starts with one practical fact: most AWS service failures arrive as ClientError, not as a long list of service-specific exception classes. That means robust code usually catches ClientError, inspects the AWS error code, and then decides whether the failure is expected, retryable, or fatal. If you skip that inspection step, your AWS automation becomes noisy and hard to trust.

Know the Main Error Categories

In day-to-day Boto3 code, you will most often see:

  • 'ClientError for service responses such as NoSuchBucket or AccessDenied'
  • 'ParamValidationError for invalid request parameters before the request is sent'
  • broader botocore exceptions for transport or configuration problems

A practical import pattern looks like this:

python
import boto3
from botocore.exceptions import BotoCoreError, ClientError, ParamValidationError

That gives you enough coverage for most application code.

Handle ClientError by Inspecting the Error Code

This is the most common pattern.

python
1import boto3
2from botocore.exceptions import ClientError
3
4s3 = boto3.client("s3")
5
6try:
7    s3.head_bucket(Bucket="example-bucket")
8    print("Bucket exists and is reachable")
9except ClientError as exc:
10    code = exc.response["Error"]["Code"]
11    message = exc.response["Error"]["Message"]
12    print(f"AWS error code: {code}")
13    print(f"AWS message: {message}")

Why inspect exc.response["Error"]["Code"]:

  • AWS services use structured error codes
  • the same Python exception type can represent many distinct service outcomes
  • application logic usually depends on the AWS code, not the Python class name alone

Example: Expected "Not Found" Versus Real Failure

Sometimes a missing resource is a normal condition, not an outage.

python
1import boto3
2from botocore.exceptions import ClientError
3
4s3 = boto3.client("s3")
5
6
7def bucket_exists(name):
8    try:
9        s3.head_bucket(Bucket=name)
10        return True
11    except ClientError as exc:
12        code = exc.response["Error"]["Code"]
13        if code in {"404", "NoSuchBucket"}:
14            return False
15        raise

This pattern is better than catching every exception and returning False, because permission errors and network problems should not be silently treated as "missing resource."

Catch Parameter Errors Separately

Some failures happen before any AWS request is even sent.

python
1from botocore.exceptions import ParamValidationError
2
3try:
4    s3.list_objects_v2(Bucket=123)  # wrong type on purpose
5except ParamValidationError as exc:
6    print("Bad request parameters:", exc)

This is a different class of bug from a service-side failure and should usually be fixed, not retried.

Log Context, Not Just the Exception String

A bare exception string is often not enough for debugging. Log the operation, the key identifiers, and the AWS error code.

python
1import logging
2from botocore.exceptions import ClientError
3
4logger = logging.getLogger(__name__)
5
6try:
7    s3.delete_object(Bucket="example-bucket", Key="reports/out.csv")
8except ClientError as exc:
9    code = exc.response["Error"]["Code"]
10    logger.error("delete_object failed for bucket=%s key=%s code=%s", "example-bucket", "reports/out.csv", code)
11    raise

That kind of log line is much more actionable in production.

Retries Need Judgment

Not every Boto3 error should be retried.

Usually retryable:

  • throttling
  • transient network failures
  • some temporary service-side issues

Usually not retryable:

  • access denied
  • validation problems
  • missing required resources when your workflow assumes they must exist

The real skill is classifying the failure correctly before adding retry loops.

A Reusable Wrapper Pattern

python
1from botocore.exceptions import BotoCoreError, ClientError
2
3
4def call_with_aws_handling(fn, *args, **kwargs):
5    try:
6        return fn(*args, **kwargs)
7    except ClientError as exc:
8        code = exc.response["Error"]["Code"]
9        raise RuntimeError(f"AWS service error: {code}") from exc
10    except BotoCoreError as exc:
11        raise RuntimeError("AWS SDK transport or configuration error") from exc

This kind of wrapper is useful when you want consistent error translation at service boundaries in your application.

Common Pitfalls

  • Catching Exception broadly and losing the structured AWS error details.
  • Treating every ClientError as identical instead of checking the AWS error code.
  • Returning fallback values for permission or throttling failures that should surface explicitly.
  • Retrying validation errors that are never going to succeed.
  • Logging only the exception string without the operation context.

Summary

  • Most AWS service failures in Boto3 arrive as ClientError.
  • Inspect exc.response["Error"]["Code"] to decide how to handle the failure.
  • Separate parameter-validation problems from service-side errors.
  • Log AWS operation context along with the error code.
  • Retry only the failures that are actually transient.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.