Error Handling & Retries
Cloud storage over the network is inherently unreliable. Connections drop, APIs get rate-limited, and reads can timeout. To provide a stable interface, s3fs includes a robust, built-in retry mechanism wrapped around its asynchronous calls.
This guide explains how s3fs handles failures internally and how you can extend that behavior for your specific use cases.
The Default Retry Strategy
Whenever s3fs communicates with S3 (e.g., calling get_object or put_object), the operation is wrapped in an _error_wrapper function.
By default, s3fs attempts the operation up to 5 times. If a retryable error occurs, it pauses execution using an exponential backoff algorithm. The wait time is calculated as min(1.7^attempt * 0.1, 15). This means delays start small (~100ms) and cap out at 15 seconds between retries.
Default Retryable Exceptions
s3fs automatically catches and retries the following transient network exceptions:
socket.timeoutbotocore.exceptions.HTTPClientErrorurllib3.exceptions.IncompleteReadfsspec.asyn.FSTimeoutErrorbotocore.parsers.ResponseParserErroraiohttp.ClientPayloadError(ifaiohttpis installed)
Additionally, it catches botocore.exceptions.ClientError and inspects the response. It automatically retries if the error message contains known rate-limiting or synchronization indicators, such as:
SlowDownreduce your request rateXAmzContentSHA256Mismatch
Extending Retry Logic
In some distributed architectures or strict enterprise environments, you might encounter custom proxies, firewalls, or internal S3 wrappers that return unique exception types or HTTP codes that s3fs does not recognize as transient.
1. Registering Custom Error Types
If you have an exception class that you know represents a temporary failure, you can add it to the global list of retryable errors using add_retryable_error.
import s3fs
from requests.exceptions import ConnectionError
# Instruct s3fs to treat requests.exceptions.ConnectionError
# as a transient, retryable failure globally.
s3fs.add_retryable_error(ConnectionError)
# Now create your filesystem
s3 = s3fs.S3FileSystem()
2. Implementing Custom Error Handlers
Sometimes simply matching an exception class isn't enough. You might need to inspect the contents of the error message to determine if it should be retried.
You can set a custom handler using set_custom_error_handler. This function must accept a single argument (the exception instance) and return True (retry) or False (do not retry and raise immediately).
import s3fs
def specific_failure_handler(e):
# Only retry if the string "Gateway Timeout" is in the error message
if isinstance(e, Exception) and "Gateway Timeout" in str(e):
return True
return False
s3fs.set_custom_error_handler(specific_failure_handler)
3. Handling AWS ClientErrors
botocore.exceptions.ClientError is a "catch-all" exception for HTTP 4xx and 5xx errors returned by AWS.
s3fs checks these against its internal patterns first. If it does not match its internal SlowDown patterns, it passes the exception to your custom error handler. You can use this to retry specific AWS error codes.
from botocore.exceptions import ClientError
import s3fs
def my_client_error_handler(e):
if isinstance(e, ClientError):
# Extract the specific AWS error code
error_code = e.response.get("Error", {}).get("Code")
# Retry if our custom infrastructure returns a specific throttling code
if error_code == "CustomThrottlingCode":
return True
return False
s3fs.set_custom_error_handler(my_client_error_handler)
Best Practices for Data Pipelines
- Fail Fast on Permissions: Do not configure custom handlers to retry
403 Forbiddenor404 Not Founderrors (unless you expect eventual consistency delays). Retrying auth failures only slows down pipeline failures. - Understand Eventual Consistency: While AWS S3 is now strongly consistent for read-after-write, some alternative S3 providers are not. If you are using an alternative provider, you might need a custom handler to retry
404 Not Founderrors briefly immediately after creating a file. - Logs are your Friend: If you suspect operations are failing and hitting the max retries, enable the debug logger (
export S3FS_LOGGING_LEVEL=DEBUG). It will print messages indicating exactly which error triggered a retry.