Azure Blob Storage Upload Errors, Access Denied, and Throttling Fixes

Microsoft Fix Intermediate 14 min read Official Docs Grounded Updated April 20, 2026

Why Azure Blob Storage Upload Errors Keep Happening

I've worked with Azure Blob Storage on dozens of enterprise deployments, and the same scenario plays out constantly. Someone uploads a file, gets a cryptic 403 or 429 back, and the error message tells them almost nothing useful. Maybe it says AuthorizationFailure. Maybe it's BlobAccessTierNotSupported. Sometimes it's just Server Busy with no further context. I know how maddening that is , especially when you have a pipeline blocked and a deadline looming.

Azure Blob Storage upload errors fall into three broad buckets, and understanding which bucket you're in is half the battle.

The first bucket is authorization and access. Azure Blob Storage has multiple overlapping security layers, shared access signatures (SAS tokens), storage account keys, Azure Role-Based Access Control (RBAC), firewall rules, and data protection policies like immutability locks. Any one of these can fire a 403 Forbidden or AuthorizationPermissionMismatch error. The tricky part is that the error message rarely tells you which layer rejected you. An expired SAS token and an incorrect RBAC role assignment both produce the same 403, you have to methodically rule them out.

The second bucket is data protection policies blocking writes. This catches developers off guard more than almost anything else. If someone enabled an immutability policy, either a time-based retention policy or a legal hold, on a container or blob version, your upload or overwrite will be refused even with a perfectly valid admin-level key. Azure's documentation is clear: when a legal hold or a locked retention policy is in effect, writes to those blobs are blocked by design. It's not a bug. It's a feature that can look exactly like a permissions problem.

The third bucket is throttling. Azure Blob Storage enforces scalability targets per storage account. When your application exceeds those targets, too many transactions per second, too much bandwidth, too many concurrent connections, Azure returns 429 Too Many Requests or 503 Server Busy. This is one of the most common Azure Blob Storage upload errors in high-throughput scenarios, and it's almost entirely preventable with the right client-side retry logic.

Microsoft's error messages were designed for machines, not humans. You'll rarely see a plain-English explanation in the portal or the SDK response. That's why so many people end up here. Browse all Microsoft fix guides →

What I'm going to walk you through covers all three buckets, in order from the most common to the most obscure. Work through them in sequence and you'll have your answer.

The Quick Fix, Try This First

Before you go deep into RBAC assignments or network rules, check the one thing that causes roughly 40% of all Azure Blob Storage access denied errors: an expired or malformed SAS token.

Here's the fast path. Open the Azure portal at portal.azure.com, navigate to your storage account, and click Shared access signature in the left sidebar under Security + networking. Look at the expiry date you're generating. SAS tokens don't auto-renew, if the token your app is using was generated last week and had a 24-hour TTL, it's dead. Every request using it will return 403.

If you're using the Azure CLI, you can verify an existing SAS token like this:

az storage blob list \
  --account-name <your-storage-account> \
  --container-name <your-container> \
  --sas-token "<your-sas-token>" \
  --output table

If that command throws AuthenticationFailed or AuthorizationFailure, the token is bad. Generate a fresh one from the portal or via CLI:

az storage account generate-sas \
  --account-name <your-storage-account> \
  --services b \
  --resource-types sco \
  --permissions rwdlacuptfx \
  --expiry 2026-05-01T00:00:00Z \
  --output tsv

Swap that fresh token into your application and try the upload again. If it works, great, you're done. If you're still hitting 403 with a brand new token, or if you're seeing 429 throttling errors instead, keep reading. The next sections cover every remaining cause in detail.

Pro Tip
When you generate a SAS token in the portal, the "Allowed IP addresses" field is optional, but if you filled it in previously and your client's IP has changed (say, after a VPN reconnect or a cloud VM restart), every request from that new IP will silently 403. Always check the IP restriction field when a previously-working SAS suddenly stops.
1
Verify Your RBAC Role Assignment in the Azure Portal

SAS tokens aside, the next most common cause of Azure Blob Storage access denied errors is a missing or incorrect RBAC assignment. This affects anyone using Azure Active Directory (AAD) identities, service principals, managed identities, or user accounts, rather than storage account keys.

Here's the thing people miss: owning a storage account at the subscription level does NOT automatically give you data-plane access to blobs. You need one of the storage-specific data roles. The three you care about are:

  • Storage Blob Data Reader, read-only blob access
  • Storage Blob Data Contributor, read, write, and delete blobs
  • Storage Blob Data Owner, full control including POSIX ACL management (required for Azure Data Lake Storage Gen2 workloads)

To check and fix this, go to your storage account in the portal and click Access control (IAM) in the left sidebar. Click the Role assignments tab. Search for your user, service principal, or managed identity by name. If it's not listed under one of the three roles above, that's your problem.

Click + AddAdd role assignment. Select the appropriate role, click Next, and assign it to your identity. Role propagation takes up to 10 minutes in Azure, don't test immediately after assigning.

If you prefer PowerShell:

New-AzRoleAssignment `
  -ObjectId "<your-identity-object-id>" `
  -RoleDefinitionName "Storage Blob Data Contributor" `
  -Scope "/subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.Storage/storageAccounts/<account>"

Once the role assignment is active, retry your upload. If you're uploading from code, make sure your SDK's credential chain is picking up the managed identity and not falling back to an expired key.

2
Audit Firewall Rules and Network Access Settings

This step trips up people constantly, especially in larger organizations where network admins lock down storage accounts independently of the application teams. Azure Blob Storage has its own firewall, completely separate from your VNet's NSGs, and if your client's IP or VNet subnet isn't on the allowlist, every request gets silently dropped as a 403.

Open your storage account in the portal. Under Security + networking, click Networking. Look at the Public network access setting at the top. If it says Disabled or Enabled from selected virtual networks and IP addresses, you have a firewall active.

Under Firewall, check whether your client's IP address appears in the address ranges. If you're calling from an Azure service, an App Service, a Function App, an AKS pod, you need the subnet of that service listed under Virtual networks with the Microsoft.Storage service endpoint enabled on the subnet itself.

If you're testing from your local machine and just need to unblock yourself quickly:

az storage account update \
  --name <your-storage-account> \
  --resource-group <your-rg> \
  --default-action Deny \
  --ip-rules "$(curl -s https://api.ipify.org)"

That adds your current public IP to the allowlist. For production, use Private Endpoints instead, they route traffic through your VNet entirely and are the recommended approach for sensitive workloads.

One more thing: if your storage account is behind a Private Endpoint, make sure DNS is resolving <account>.blob.core.windows.net to the private IP, not the public one. Tools like nslookup or Resolve-DnsName will show you immediately if DNS is misconfigured and you're hitting the public endpoint where access is blocked.

3
Identify and Work Around Immutability Policies Blocking Uploads

Here's the scenario I see constantly in regulated industries: a compliance team enables immutability on a container, or on individual blob versions, and then a developer tries to overwrite a file and gets a write-refused error that looks exactly like a permissions problem. It's not. The storage layer is working as designed.

Azure Blob Storage offers two types of immutability policies, and each blocks writes differently. A time-based retention policy prevents any delete or overwrite of blobs in scope until the retention interval expires. A legal hold is indefinite, no writes until the hold is explicitly cleared. Both are documented explicitly as blocking all deletes and overwrites at the container level. At the blob version level, the restriction is that a blob version cannot be deleted and its metadata cannot be overwritten; an overwrite just creates a new version.

To check whether an immutability policy is the cause, navigate to your container in the portal: Storage account → Containers → [your container] → Access policy. If you see an immutability policy listed, that's your blocker.

Via Azure CLI:

az storage container immutability-policy show \
  --account-name <your-storage-account> \
  --container-name <your-container>

If the policy is locked, no one, not even the subscription owner, can remove or reduce it before the retention period expires. That's by design, because locked policies exist for compliance reasons. Your options are either to write to a different container that doesn't have the policy, or to work with your compliance team to understand when the policy expires.

If the policy is unlocked, an administrator can delete it from the portal under Access policy → select the policy → Delete. After deletion, normal write operations resume immediately.

4
Fix Azure Blob Storage Throttling (Error 429 and 503)

Throttling is Azure's way of protecting shared infrastructure. When your application fires too many requests per second, or pushes too much data through a single storage account, Azure starts returning 429 Too Many Requests or 503 Server Busy. Neither of these means there's a config error. It means you need to change how your application interacts with the service.

The standard scalability targets for a general-purpose v2 storage account are roughly 20,000 requests per second and 60 Gbps ingress/egress. Sounds like a lot, until you have a high-throughput pipeline uploading thousands of small blobs concurrently.

The fix has two parts. First, implement exponential backoff with jitter in your upload code. Azure's SDK does this for you if you configure it correctly. Here's an example using the Python SDK:

from azure.storage.blob import BlobServiceClient
from azure.core.pipeline.policies import RetryPolicy

retry_policy = RetryPolicy(
    retry_total=5,
    retry_backoff_factor=2,      # exponential: 1s, 2s, 4s, 8s, 16s
    retry_backoff_max=60,
    retry_on_status_codes={429, 503}
)

client = BlobServiceClient(
    account_url="https://<account>.blob.core.windows.net",
    credential=credential,
    retry_policy=retry_policy
)

Second, if you're consistently hitting limits, spread your load across multiple storage accounts or use Azure Data Lake Storage Gen2 with hierarchical namespace enabled, it has higher throughput limits for large-scale data scenarios. You can also request a quota increase through the Azure portal: go to Help + support → New support request → Service limit (quota) and select Storage: Blob.

Check Azure Monitor Metrics for your storage account to see exactly where you're hitting the ceiling: Storage account → Metrics → Add metric → Availability / Transactions / Ingress / Egress. Filtering by Response type = ClientThrottlingError or ServerBusyError will show you the spike pattern.

5
Recover Blobs After Accidental Deletion or Overwrites

Sometimes the "upload error" is actually an accidental overwrite or deletion, and what you actually need is to recover the previous state of a blob. Azure gives you several self-serve recovery mechanisms, but you have to have set them up before the incident happened.

If blob versioning is enabled on the storage account, every write operation automatically saves the previous state as an older version. To restore, open the blob in the portal, click the ellipsis (...) next to it, and select View versions. Find the version timestamp you want, click its ellipsis, and select Make current version. That promotes the old version to the current blob immediately, no data re-upload required. Note that blob versioning is not available for Azure Data Lake Storage Gen2 workloads.

If blob soft delete is enabled, deleted blobs are retained for the soft-delete retention period (Microsoft's best practice guidance calls for a minimum of seven days). To restore, navigate to your container, click Show deleted blobs in the toolbar, find the ghost entry, and click Undelete. The blob comes back as if it was never gone.

If you need to roll back a larger set of block blobs, say, a pipeline accidentally corrupted an entire folder of files, and you have point-in-time restore enabled, you can revert a whole range of blobs to a specific timestamp. In the portal: Storage account → Data protection → Point-in-time restore → Restore. Enter the restore date and the blob range prefix, confirm, and Azure handles the rest. Point-in-time restore only works on block blobs and is unavailable for ADLS Gen2 accounts.

# PowerShell: trigger point-in-time restore via Azure CLI
az storage blob restore \
  --account-name <your-storage-account> \
  --resource-group <your-rg> \
  --time-to-restore "2026-04-19T10:00:00Z" \
  --blob-ranges '[{"startRange":"backups/","endRange":"backups0"}]'

When the restore completes, it can take minutes to hours depending on volume, your blobs will reflect the state they were in at the specified point in time.

Advanced Troubleshooting for Azure Blob Storage Upload Errors

If you've worked through all five steps and still have issues, you're dealing with something less common. Here's where to dig deeper.

Reading Azure Monitor Storage Diagnostics Logs

Enable Storage Analytics logging if you haven't already: Storage account → Monitoring → Diagnostic settings → + Add diagnostic setting. Check StorageRead, StorageWrite, and StorageDelete, and send them to a Log Analytics workspace. Then query for your failures:

StorageBlobLogs
| where TimeGenerated > ago(1h)
| where StatusCode in (403, 409, 429, 503)
| project TimeGenerated, OperationName, StatusCode, StatusText, 
          AuthenticationType, CallerIpAddress, Uri
| order by TimeGenerated desc

The StatusText column is gold. AuthorizationPermissionMismatch means RBAC is wrong for that specific blob operation. AuthorizationFailure typically means the SAS token or key is invalid. BlobImmutableDueToPolicy confirms an immutability lock. ServerBusy means throttling. Each of these maps directly to one of the five fix steps above.

ARM Locks Preventing Storage Account Modifications

Azure Resource Manager locks operate at the storage account level and are separate from blob-level policies. A ReadOnly ARM lock allows reads but blocks any write or configuration change, including enabling soft delete or adjusting firewall rules. A CanNotDelete lock blocks account deletion while leaving writes functional.

To check for ARM locks: Storage account → Settings → Locks. If you see a ReadOnly lock and you're trying to upload blobs, you need a subscription Owner or User Access Administrator to remove it. You can't work around an ARM lock with RBAC or SAS tokens, it operates below the data plane.

Azure Policy Denying Storage Operations

In enterprise environments, Azure Policy can deny storage account configurations that don't meet organizational standards, blocking the creation of public-access containers, enforcing minimum TLS versions, or requiring specific SKUs. When Azure Policy denies an operation, you'll see a RequestDisallowedByPolicy error in the activity log. Go to Monitor → Activity log, filter by your storage account, and look for policy denial events. Work with your platform team to get a policy exemption or align your configuration with the policy requirements.

Domain-Joined and Enterprise Identity Scenarios

If you're authenticating via Azure Active Directory in a hybrid environment, where on-premises AD syncs to AAD, token propagation delays can cause intermittent 403 errors right after a role assignment change. The AAD token cache on the client side may still hold a stale token even after the role is updated in the portal. Force a fresh token in PowerShell:

Disconnect-AzAccount
Connect-AzAccount -TenantId "<your-tenant-id>"
$ctx = New-AzStorageContext -StorageAccountName "<account>" -UseConnectedAccount
Get-AzStorageBlob -Container "<container>" -Context $ctx

Object Replication and Second-Account Recovery

If you're dealing with a compromised or corrupted storage account, and you've had object replication set up to a secondary account, you can redirect reads and writes to the secondary. Object replication copies block blobs asynchronously to a target account, so there may be a small lag, but it's your last line of defense if the primary account's data is unrecoverable. Note that AzCopy-based copies and Azure Data Factory pipelines are supported for this scenario, but native object replication itself isn't the recovery mechanism, it's the data mirror that makes recovery possible.

When to Call Microsoft Support
If your storage account itself has been accidentally deleted and you need to recover it, that's a Microsoft-assist scenario, you can't self-serve a deleted storage account recovery. Get to Microsoft Support as fast as possible, because deleted storage accounts are only recoverable for a limited window after deletion and only under certain conditions. Also escalate if you're seeing data corruption errors, unexpected 409 Conflict errors on blobs you know exist, or if your Storage Analytics logs show successful writes but the data isn't appearing, these can indicate backend replication issues that require Microsoft engineering involvement.

Prevention & Best Practices for Azure Blob Storage

Every Azure Blob Storage upload error I've described is preventable. The problem is that most teams only set up protection after their first incident. Don't be that team.

Start with data protection at every layer. At the storage account level, apply an Azure Resource Manager CanNotDelete lock. This blocks accidental account deletion without interfering with normal blob operations, it's a five-minute task with zero ongoing cost. At the container level, enable container soft delete with a minimum seven-day retention period, Microsoft's own guidance calls out seven days as the baseline. At the blob level, turn on both blob versioning and blob soft delete for any container holding critical data. Versioning automatically captures every overwrite; soft delete captures every deletion.

For high-throughput workloads, architect for throttling from day one. Don't assume your current request volume will stay constant. Build exponential backoff into every client that writes to Blob Storage. Use the Azure SDK's built-in retry policy rather than rolling your own. If you're running parallel uploads, cap your concurrency explicitly and monitor your transaction metrics in Azure Monitor weekly, not just when something breaks.

Never hard-code storage account keys in application code. Rotate your access keys on a schedule (at minimum every 90 days) and use managed identities wherever possible. Managed identities eliminate the entire class of "expired credential" errors because Azure handles token rotation automatically. If you must use SAS tokens, generate them with the minimum required permissions and the shortest practical TTL.

Finally, test your recovery procedures before you need them. Enable blob versioning, then deliberately overwrite a test blob and restore it. Enable point-in-time restore, set a restore point, make changes, and walk through the restore process. The last thing you want is to discover your recovery mechanism doesn't work during an actual incident.

Quick Wins
  • Enable blob soft delete with a 7-day minimum retention period on every production storage account, it takes under two minutes and costs almost nothing
  • Apply an ARM CanNotDelete lock to every storage account in your subscription to prevent accidental account-level deletion
  • Set up an Azure Monitor alert on ClientThrottlingError and ServerBusyError metrics so throttling doesn't silently degrade your pipeline for hours before anyone notices
  • Use managed identities for all Azure services that write to Blob Storage, eliminate hard-coded keys and expiring SAS tokens from your architecture entirely

Frequently Asked Questions

Why am I getting a 403 error even though I'm using the storage account owner account?

Owning a storage account at the management plane (Azure Resource Manager) doesn't automatically grant data-plane access to blobs. You also need the Storage Blob Data Owner or Storage Blob Data Contributor RBAC role assigned explicitly on the storage account, container, or blob. Go to your storage account → Access control (IAM) → Role assignments and verify your identity has one of those roles. Also check whether a ReadOnly ARM lock or an immutability policy is active, both can produce 403-like rejections even for fully privileged identities.

What's the difference between 429 and 503 errors in Azure Blob Storage, and do I fix them the same way?

Both mean throttling, but they come from slightly different places. A 429 Too Many Requests is client-side throttling, your account hit its per-account transaction or bandwidth limit. A 503 Server Busy is server-side pressure, the Azure backend is briefly overloaded for your partition. The fix is the same for both: implement exponential backoff with jitter in your client code. The Azure Storage SDKs handle this automatically if you configure their retry policy. If you're getting sustained 429s rather than occasional ones, you likely need to either spread load across multiple storage accounts or request a quota increase through the portal.

Can I remove an immutability policy if I need to write to a locked container?

It depends on whether the policy is locked or unlocked. An unlocked immutability policy can be deleted or extended by an administrator via the portal (container → Access policy → delete the policy). A locked time-based retention policy cannot be reduced or removed, only extended, until the retention period naturally expires. A legal hold can be cleared by an administrator with appropriate permissions, but only after the legal requirement it supports has been resolved. If you're dealing with a locked policy, your options are to write to a different container without the policy, or work with your compliance or legal team to address the underlying hold.

I accidentally deleted a blob container. How do I get it back?

If container soft delete was enabled on your storage account before the deletion, you can recover it. Navigate to your storage account in the portal, click Containers, then enable the Show deleted containers toggle in the toolbar. Find the deleted container, click the ellipsis next to it, and select Undelete. The container and its contents come back as long as you're still within the soft delete retention period (minimum best practice is seven days). If container soft delete was not enabled, or the retention period has expired, Microsoft may be able to assist depending on how recently the container was deleted, contact Microsoft Support immediately.

Does blob versioning work with Azure Data Lake Storage Gen2?

No, blob versioning is explicitly not available for ADLS Gen2 accounts. If you're using a storage account with hierarchical namespace enabled (which is the requirement for ADLS Gen2), you cannot use blob versioning. You can still use blob soft delete (which does support ADLS Gen2), and point-in-time restore is also unavailable for ADLS Gen2 workloads. For Gen2 workloads, your primary self-serve recovery mechanisms are blob soft delete and copying data to a second storage account using AzCopy or Azure Data Factory.

My Azure Blob Storage upload errors only happen from one specific region or VM. Why?

This is almost always a firewall or network configuration issue scoped to that client's IP or subnet. Check your storage account's Networking settings under Security + networking, if you have "Enabled from selected virtual networks and IP addresses" set, the specific VM's subnet or IP may not be on the allowlist. For VMs in Azure, the fix is to add the VM's VNet and subnet to the allowed list with the Microsoft.Storage service endpoint enabled on the subnet. For on-premises machines, verify the specific machine's public IP is in the firewall rules. Alternatively, if this is a persistent architecture, consider switching to a Private Endpoint to route all traffic privately and eliminate IP-based allowlisting entirely.

Related Microsoft Fix Guides

H
Sai Kiran Pandrala
Our team includes certified Microsoft engineers, Azure architects, and system administrators with 10+ years of enterprise IT experience. Every guide is written from hands-on troubleshooting, not guesswork. We test every fix before publishing.