Troubleshooting Blob Storage: Common Issues and Solutions
Blob storage is a foundational service in modern cloud applications, used for storing unstructured data such as images, videos, documents, backups, and logs. Whether you are using Azure Blob Storage, AWS S3, or Google Cloud Storage, developers frequently encounter similar categories of issues: authentication failures, network timeouts, missing blobs, CORS errors, and throttling. This tutorial walks through the most common Blob Storage problems, explains why they occur, and provides practical solutions with code examples you can apply immediately.
Why Troubleshooting Blob Storage Matters
Blob storage is often the silent backbone of an application. When it fails, the symptoms can be subtle: broken images on a website, failed file uploads, or delayed report generation. Because blob storage interacts with many layers of your stack — frontend, backend, CDN, and identity providers — pinpointing the root cause requires a systematic approach. Understanding common failure modes helps you reduce downtime, improve user experience, and avoid costly data loss.
1. Authentication and Authorization Errors
One of the most frequent issues developers face is the dreaded 403 Forbidden or AuthenticationFailed error. These occur when the credentials used to access the blob are invalid, expired, or lack the necessary permissions.
Common Causes
- Expired Shared Access Signature (SAS) tokens
- Incorrect account key or connection string
- Missing role assignments when using Azure AD / Managed Identity
- Clock skew between client and server
Solution: Generate a Valid SAS Token
When using SAS tokens, ensure the token has not expired and includes the necessary permissions (r for read, w for write, d for delete). Here is an example using the Azure Storage SDK for Python:
from datetime import datetime, timedelta
from azure.storage.blob import BlobServiceClient, generate_blob_sas, BlobSasPermissions
account_name = "mystorageaccount"
account_key = "your_account_key"
container_name = "mycontainer"
blob_name = "example.txt"
sas_token = generate_blob_sas(
account_name=account_name,
container_name=container_name,
blob_name=blob_name,
account_key=account_key,
permission=BlobSasPermissions(read=True),
expiry=datetime.utcnow() + timedelta(hours=1),
start=datetime.utcnow() - timedelta(minutes=5) # allow for clock skew
)
blob_url = f"https://{account_name}.blob.core.windows.net/{container_name}/{blob_name}?{sas_token}"
print(blob_url)
Notice the start time is set slightly in the past. This accounts for clock skew between the client machine and the storage service, which is a surprisingly common cause of authentication failures.
Using Managed Identity Instead of Keys
For production workloads, prefer Managed Identity over account keys. This eliminates the risk of leaked credentials:
from azure.identity import DefaultAzureCredential
from azure.storage.blob import BlobServiceClient
credential = DefaultAzureCredential()
blob_service_client = BlobServiceClient(
account_url="https://mystorageaccount.blob.core.windows.net",
credential=credential
)
blob_client = blob_service_client.get_blob_client(
container="mycontainer",
blob="example.txt"
)
with open("example.txt", "rb") as data:
blob_client.upload_blob(data)
print("Upload successful")
Ensure the Managed Identity has the Storage Blob Data Contributor role assigned at the storage account, container, or blob level.
2. Blob Not Found (404) Errors
A 404 Not Found error is straightforward but can be tricky to debug when you are certain the blob exists. The issue often comes down to case sensitivity, container naming, or soft-deleted blobs.
Common Causes
- Case-sensitive blob names (e.g.,
Image.PNGvsimage.png) - Incorrect container name
- Blob was soft-deleted and is in the retention period
- Wrong storage account (especially in multi-tenant apps)
Solution: Verify Blob Existence and Handle Soft Deletes
from azure.storage.blob import BlobServiceClient
from azure.core.exceptions import ResourceNotFoundError
blob_service_client = BlobServiceClient.from_connection_string(conn_str)
blob_client = blob_service_client.get_blob_client("mycontainer", "Image.PNG")
try:
props = blob_client.get_blob_properties()
print(f"Blob exists. Size: {props.size} bytes")
except ResourceNotFoundError:
print("Blob not found. Checking for soft-deleted blobs...")
# List soft-deleted blobs
container_client = blob_service_client.get_container_client("mycontainer")
deleted_blobs = container_client.list_blobs(include_deleted=True)
for b in deleted_blobs:
if b.name == "Image.PNG" and b.deleted:
print(f"Found soft-deleted blob. Restoring...")
blob_client.undelete_blob()
print("Blob restored successfully.")
break
Enabling soft delete on your storage account gives you a safety net for accidental deletions. You can configure the retention period from 1 to 365 days.
3. Connection Timeouts and Network Issues
Timeouts can occur during large uploads, downloads, or when the network between your application and the storage service is unreliable. The default timeout in most SDKs is 30 seconds, which may be insufficient for large files.
Solution: Configure Timeouts and Use Retry Policies
from azure.storage.blob import BlobServiceClient, BlobClient
from azure.core.pipeline.policies import RetryPolicy
custom_retry = RetryPolicy(
total_retries=5,
backoff_factor=2,
retry_total=5
)
blob_service_client = BlobServiceClient.from_connection_string(
conn_str,
retry_policy=custom_retry
)
blob_client = blob_service_client.get_blob_client("mycontainer", "large-file.zip")
with open("large-file.zip", "rb") as data:
blob_client.upload_blob(
data,
timeout=300, # 5 minute timeout
overwrite=True
)
print("Large file uploaded successfully.")
Using Block Blobs for Large Files
For files larger than a few hundred megabytes, use block-based uploads. This allows parallel uploads and resumable transfers:
from azure.storage.blob import BlobClient
import os
blob_client = BlobClient.from_blob_url(blob_url_with_sas)
file_path = "large-video.mp4"
chunk_size = 4 * 1024 * 1024 # 4 MB chunks
with open(file_path, "rb") as f:
block_ids = []
index = 0
while True:
chunk = f.read(chunk_size)
if not chunk:
break
block_id = f"{index:06d}"
blob_client.stage_block(block_id, chunk)
block_ids.append(block_id)
index += 1
print(f"Uploaded block {index}")
blob_client.commit_block_list(block_ids)
print(f"Upload complete. Total blocks: {index}")
If a chunk fails, you only need to re-upload that specific block, not the entire file.
4. CORS Errors from Browser Applications
When accessing blob storage directly from a browser (e.g., uploading files from a React or Vue frontend), you may encounter CORS errors in the browser console. This happens because the storage account has not been configured to allow cross-origin requests from your domain.
Solution: Configure CORS Rules
You can configure CORS rules through the Azure Portal, Azure CLI, or programmatically:
from azure.storage.blob import BlobServiceClient
blob_service_client = BlobServiceClient.from_connection_string(conn_str)
cors_rules = [{
"allowed_origins": ["https://myapp.example.com", "http://localhost:3000"],
"allowed_methods": ["GET", "PUT", "POST", "DELETE", "HEAD"],
"allowed_headers": ["*"],
"exposed_headers": ["ETag", "Content-Length"],
"max_age_in_seconds": 3600
}]
service_properties = blob_service_client.set_service_properties(cors=cors_rules)
print("CORS rules configured successfully.")
Key points to remember:
- Be specific with allowed origins — avoid using
*in production - Include
http://localhost:3000(or your dev port) for local development - The
max_age_in_secondsvalue controls how long the browser caches preflight responses
5. Throttling (429 Too Many Requests)
Storage accounts have scalability targets. For standard general-purpose accounts, the target is up to 20,000 requests per second. When you exceed this limit, the service returns a 429 status code. This is common in batch processing, log ingestion, or high-traffic web applications.
Common Causes
- Too many concurrent requests on a single partition
- Polling loops that check for blob existence too frequently
- Bulk operations without rate limiting
Solution: Implement Exponential Backoff and Partitioning
import time
from azure.storage.blob import BlobServiceClient
from azure.core.exceptions import ResourceExistsError
blob_service_client = BlobServiceClient.from_connection_string(conn_str)
def upload_with_retry(container_name, blob_name, data, max_retries=5):
blob_client = blob_service_client.get_blob_client(container_name, blob_name)
for attempt in range(max_retries):
try:
blob_client.upload_blob(data, overwrite=True)
print(f"Upload successful on attempt {attempt + 1}")
return True
except Exception as e:
if "429" in str(e) or "ServerBusy" in str(e):
wait_time = (2 ** attempt) + 0.5 # exponential backoff with jitter
print(f"Throttled. Retrying in {wait_time:.1f}s (attempt {attempt + 1})")
time.sleep(wait_time)
else:
raise e
raise Exception(f"Failed after {max_retries} retries")
upload_with_retry("mycontainer", "data.json", b'{"key": "value"}')
For high-throughput scenarios, distribute blobs across multiple containers or use a naming convention that spreads them across different partitions. Avoid naming all blobs with the same prefix (e.g., timestamps like 2024-01-01-log-001), as this can cause hot partition issues.
6. Accidental Overwrites and Data Corruption
When multiple processes write to the same blob simultaneously, or when a bug overwrites data unexpectedly, you can lose critical information. This is especially dangerous in shared containers.
Solution: Use Lease and Versioning
A lease provides exclusive write access to a blob for a specified duration:
from azure.storage.blob import BlobClient, LeaseClient
blob_client = BlobClient.from_blob_url(blob_url_with_sas)
lease_client = LeaseClient(blob_client)
# Acquire a lease for 60 seconds
lease = lease_client.acquire(lease_duration=60)
print(f"Lease acquired: {lease}")
try:
# Only the lease holder can write to this blob
blob_client.upload_blob(b"new data", lease=lease)
print("Data written with lease protection.")
finally:
lease_client.release()
print("Lease released.")
Additionally, enable blob versioning on your storage account. This automatically preserves previous versions of a blob every time it is modified, giving you point-in-time recovery without any code changes.
Best Practices for Blob Storage Reliability
- Use Managed Identity instead of account keys or SAS tokens whenever possible to reduce credential management overhead.
- Enable soft delete and versioning on all production storage accounts to protect against accidental deletion and overwrites.
- Implement retry logic with exponential backoff for all storage operations to handle transient failures gracefully.
- Use block blobs for large files to enable parallel uploads and resumable transfers.
- Configure CORS explicitly and avoid wildcard origins in production environments.
- Monitor storage metrics using Azure Monitor or equivalent tools to detect throttling, latency spikes, and capacity issues proactively.
- Use meaningful naming conventions that distribute blobs across partitions to avoid hot spots.
- Validate blob existence before operations and handle
ResourceNotFoundErrorexceptions explicitly. - Set appropriate access tiers (Hot, Cool, Archive) based on access patterns to optimize cost without sacrificing availability.
Conclusion
Troubleshooting Blob Storage issues requires a combination of understanding the underlying service mechanics and implementing defensive coding practices. By addressing authentication errors proactively, handling 404s with soft-delete recovery, configuring proper timeouts and retry policies, resolving CORS configuration, and guarding against throttling and data corruption, you can build robust applications that handle storage operations reliably. The key is to never assume storage operations will always succeed — always implement error handling, retries, and monitoring. With the strategies and code examples in this tutorial, you are well-equipped to diagnose and resolve the most common Blob Storage problems in your applications.