The storage/retry-limit-exceeded error occurs when Firebase Storage's automatic retry mechanism reaches its maximum timeout while attempting an upload, download, or delete operation. This typically happens with large files, poor network connectivity, or insufficient timeout configurations.
The `storage/retry-limit-exceeded` error indicates that Firebase Storage has exhausted its built-in retry mechanism for an operation. Firebase Storage automatically retries failed operations to handle transient network issues, but when operations repeatedly fail or take too long, it eventually gives up and throws this error. By default, Firebase Storage retries operations for up to 10 minutes (600,000 milliseconds) before throwing this error. The retry mechanism uses exponential backoff, progressively waiting longer between retry attempts. This error most commonly occurs during uploads or downloads of large files, especially on poor or intermittent network connections. The error can occur with any storage operation: uploads (putFile, putBytes), downloads (getDownloadURL, getData), and delete operations. When you encounter this error, it means Firebase has exhausted all retry attempts within the configured time window and cannot complete the operation.
Configure Firebase Storage to allow more time for file operations by increasing the maximum retry time.
For Web/JavaScript:
import { getStorage, ref } from 'firebase/storage';
const storage = getStorage();
// Increase max retry times to 20 minutes (1200000ms)
storage.maxUploadRetryTime = 1200000; // 20 minutes
storage.maxDownloadRetryTime = 1200000; // 20 minutes
storage.maxOperationRetryTime = 1200000; // 20 minutes for other operationsFor Android:
import com.google.firebase.storage.FirebaseStorage;
FirebaseStorage storage = FirebaseStorage.getInstance();
// Set max upload retry time to 20 minutes (1200000ms)
storage.setMaxUploadRetryTimeMillis(1200000L);
// Set max operation retry time to 20 minutes
storage.setMaxOperationRetryTimeMillis(1200000L);For React Native Firebase:
import storage from '@react-native-firebase/storage';
// Set timeout on storage instance
const storageRef = storage().ref('path/to/file');
storageRef.setMaxUploadRetryTime(1200000); // 20 minutes
storageRef.setMaxDownloadRetryTime(1200000);For Flutter (FlutterFire):
import 'package:firebase_storage/firebase_storage.dart';
FirebaseStorage storage = FirebaseStorage.instance;
// Create upload task with custom timeout
final uploadTask = storage
.ref('path/to/file')
.putFile(file);Start with a 15-20 minute timeout and adjust based on your typical file sizes and network conditions.
Add application-level retry logic that catches the error and retries with increasing delays between attempts.
import { getStorage, ref, uploadBytesResumable } from 'firebase/storage';
async function uploadWithRetry(file, path, maxRetries = 3) {
const storage = getStorage();
const storageRef = ref(storage, path);
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const uploadTask = uploadBytesResumable(storageRef, file);
return new Promise((resolve, reject) => {
uploadTask.on('state_changed',
(snapshot) => {
const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log(`Upload progress: ${progress}%`);
},
(error) => {
if (error.code === 'storage/retry-limit-exceeded' && attempt < maxRetries) {
console.log(`Attempt ${attempt} failed with retry-limit-exceeded, retrying...`);
reject(error);
} else {
reject(error);
}
},
() => {
console.log('Upload successful');
resolve(uploadTask.snapshot);
}
);
});
} catch (error) {
if (error.code === 'storage/retry-limit-exceeded' && attempt < maxRetries) {
// Calculate exponential backoff: 2^attempt seconds
const waitMs = Math.pow(2, attempt) * 1000;
console.log(`Retrying in ${waitMs}ms (attempt ${attempt}/${maxRetries})`);
await new Promise(resolve => setTimeout(resolve, waitMs));
} else {
throw error; // Re-throw if last attempt or different error
}
}
}
throw new Error(`Upload failed after ${maxRetries} attempts`);
}This approach waits 2 seconds before retry 1, 4 seconds before retry 2, and 8 seconds before retry 3.
For large files, use resumable upload API which allows pause/resume capability and better handles network interruptions.
import { getStorage, ref, uploadBytesResumable } from 'firebase/storage';
function uploadLargeFile(file, path) {
const storage = getStorage();
const storageRef = ref(storage, path);
// uploadBytesResumable automatically handles large files
const uploadTask = uploadBytesResumable(storageRef, file);
// Monitor upload progress
uploadTask.on('state_changed',
(snapshot) => {
const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log(`Upload is ${progress}% done`);
// You can pause the upload if needed
// uploadTask.pause();
},
(error) => {
if (error.code === 'storage/retry-limit-exceeded') {
console.error('Upload timeout. Try again or increase maxUploadRetryTime.');
} else {
console.error('Upload error:', error);
}
},
() => {
console.log('Upload completed successfully');
// Handle successful upload
}
);
return uploadTask;
}
// Usage
const fileInput = document.getElementById('fileInput');
const uploadBtn = document.getElementById('uploadBtn');
uploadBtn.addEventListener('click', () => {
const file = fileInput.files[0];
if (file) {
uploadLargeFile(file, `uploads/${file.name}`);
}
});Resumable uploads are more resilient to network interruptions and are recommended for files over 5MB.
Detect network changes and automatically pause/resume uploads to avoid timeouts during connection switches.
import { getStorage, ref, uploadBytesResumable } from 'firebase/storage';
let uploadTask = null;
// Handle network status changes
window.addEventListener('online', () => {
console.log('Network restored');
if (uploadTask && uploadTask.snapshot.state === 'paused') {
console.log('Resuming upload...');
uploadTask.resume();
}
});
window.addEventListener('offline', () => {
console.log('Network lost');
if (uploadTask && uploadTask.snapshot.state === 'running') {
console.log('Pausing upload to prevent timeout...');
uploadTask.pause();
}
});
function startUpload(file, path) {
const storage = getStorage();
const storageRef = ref(storage, path);
uploadTask = uploadBytesResumable(storageRef, file);
uploadTask.on('state_changed',
(snapshot) => {
console.log(`Progress: ${snapshot.bytesTransferred}/${snapshot.totalBytes}`);
// Update UI with progress
},
(error) => {
if (error.code === 'storage/retry-limit-exceeded') {
console.error('Operation timed out. Check network and retry.');
} else {
console.error('Upload error:', error);
}
},
() => {
console.log('Upload complete');
uploadTask = null;
}
);
}This is especially important for mobile applications that may switch between WiFi and cellular networks during uploads.
Check file size and network speed before attempting uploads to prevent timeout errors.
// Define maximum file sizes based on expected connection speed
const MAX_FILE_SIZE = 100 * 1024 * 1024; // 100MB
const RECOMMENDED_FILE_SIZE_WIFI = 500 * 1024 * 1024; // 500MB
const RECOMMENDED_FILE_SIZE_CELLULAR = 20 * 1024 * 1024; // 20MB
// Detect connection type (works in modern browsers)
function getConnectionType() {
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
if (!connection) return 'unknown';
return connection.effectiveType; // 4g, 3g, 2g, slow-2g
}
function validateBeforeUpload(file) {
const connectionType = getConnectionType();
if (file.size > MAX_FILE_SIZE) {
alert(`File too large (${(file.size / 1024 / 1024).toFixed(2)}MB). Maximum is ${(MAX_FILE_SIZE / 1024 / 1024)}MB.`);
return false;
}
if (connectionType === '2g' || connectionType === 'slow-2g') {
alert('Connection too slow for large file uploads. Please use WiFi.');
return false;
}
if (connectionType === '3g' && file.size > RECOMMENDED_FILE_SIZE_CELLULAR) {
alert(`File large for cellular connection. Recommended max: ${(RECOMMENDED_FILE_SIZE_CELLULAR / 1024 / 1024)}MB`);
return false;
}
return true;
}
// Before upload
const fileInput = document.getElementById('fileInput');
const uploadBtn = document.getElementById('uploadBtn');
uploadBtn.addEventListener('click', () => {
const file = fileInput.files[0];
if (file && validateBeforeUpload(file)) {
startUpload(file, `uploads/${file.name}`);
}
});Pre-validation prevents users from starting uploads that are likely to fail due to network or file size constraints.
SDK-Specific Default Timeouts:
Different Firebase SDKs have different default retry timeout values:
- Web/JavaScript SDK: 10 minutes (600,000ms) for all operations
- Android SDK: 10 minutes for uploads, 10 minutes for other operations
- iOS SDK: Similar 10-minute defaults with platform-specific tuning options
- Flutter: Inherits platform defaults (Android/iOS)
- React Native: Uses native Firebase SDKs underneath
Resumable Upload Sessions:
Firebase Storage uses resumable upload sessions for files larger than 256KB. These sessions remain valid for 1 week, allowing uploads to be resumed even after app restarts. However, the retry-limit-exceeded error is based on cumulative retry time within a single upload session, not the total session lifetime.
ISP/Network Blocking:
In rare cases, some ISPs or corporate firewalls block connections to Firebase Storage domains. If you suspect this is the issue:
- Test from a different network (mobile hotspot, different WiFi)
- Check Firebase Status Dashboard for any service issues
- Consider using Firebase Cloud Functions to handle uploads server-side where network is more controlled
Large File Best Practices:
For files exceeding 100MB:
- Use resumable uploads exclusively (uploadBytesResumable)
- Implement compression before upload (images, videos)
- Consider splitting into multiple smaller uploads
- Use Firebase Cloud Functions to coordinate multi-part uploads
- Monitor network status and implement pause/resume
- Consider server-side processing instead of client uploads
Exponential Backoff Strategy:
The built-in retry uses exponential backoff internally. If adding custom retry logic, use exponential backoff to avoid overwhelming the network:
- Start with 2-4 second delays
- Double the delay with each retry
- Cap at 30-60 seconds to prevent excessive delays
- Limit total retries to 3-5 attempts
Performance Monitoring:
Track upload metrics to optimize timeout settings:
const startTime = Date.now();
uploadTask.on('state_changed',
(snapshot) => {
const elapsed = (Date.now() - startTime) / 1000;
const uploadedMB = snapshot.bytesTransferred / (1024 * 1024);
const uploadSpeedMbps = (uploadedMB * 8) / elapsed;
console.log(`Upload speed: ${uploadSpeedMbps.toFixed(2)} Mbps`);
}
);This helps you understand typical upload speeds and set appropriate timeout values.
messaging/UNSPECIFIED_ERROR: No additional information available
How to fix "messaging/UNSPECIFIED_ERROR: No additional information available" in Firebase Cloud Messaging
App Check: reCAPTCHA Score Too Low
App Check reCAPTCHA Score Too Low
storage/invalid-url: Invalid URL format for Cloud Storage reference
How to fix invalid URL format in Firebase Cloud Storage
auth/missing-uid: User ID identifier required
How to fix "auth/missing-uid: User ID identifier required" in Firebase
auth/invalid-argument: Invalid parameter passed to method
How to fix "auth/invalid-argument: Invalid parameter passed to method" in Firebase