Quick Answer
Fix storage quota errors: check available space, implement storage cleanup, and handle storage limits gracefully.
Understanding the Issue
The "QuotaExceededError" happens when web storage (localStorage or sessionStorage) exceeds its allocated space limit, typically 5-10MB per origin. This error occurs when trying to store large amounts of data or when storage is nearly full. Different browsers have different limits, and the error can also occur in private/incognito mode where storage limits are more restrictive. Understanding storage limits, implementing cleanup strategies, and graceful error handling prevents application failures.
The Problem
This code demonstrates the issue:
Javascript
Error
-- Problem 1: Storing large data without checking limits
const largeData = new Array(10000000).fill("data").join(""); -- Very large string
localStorage.setItem("largeData", largeData); -- QuotaExceededError
-- Problem 2: No cleanup strategy for accumulated data
for (let i = 0; i < 10000; i++) {
localStorage.setItem(`item_${i}`, `data for item ${i}`);
-- Eventually hits quota limit
}
The Solution
Here's the corrected code:
Javascript
Fixed
-- Solution 1: Safe storage with quota checking
function safeSetItem(key, value, storage = localStorage) {
try {
storage.setItem(key, value);
return true;
} catch (error) {
if (error.name === "QuotaExceededError") {
console.warn("Storage quota exceeded");
handleStorageQuotaExceeded(storage);
return false;
}
throw error;
}
}
function handleStorageQuotaExceeded(storage) {
-- Strategy 1: Clear old items
clearOldStorageItems(storage);
-- Strategy 2: Remove least important items
removeLeastImportantItems(storage);
-- Strategy 3: Notify user
notifyUserOfStorageLimit();
}
function clearOldStorageItems(storage, maxAge = 7 * 24 * 60 * 60 * 1000) { -- 7 days
const now = Date.now();
const keysToRemove = [];
for (let i = 0; i < storage.length; i++) {
const key = storage.key(i);
const item = storage.getItem(key);
try {
const data = JSON.parse(item);
if (data.timestamp && (now - data.timestamp) > maxAge) {
keysToRemove.push(key);
}
} catch (error) {
-- If not JSON or no timestamp, consider for removal
keysToRemove.push(key);
}
}
keysToRemove.forEach(key => storage.removeItem(key));
console.log(`Removed ${keysToRemove.length} old items`);
}
-- Solution 2: Storage management utilities
class StorageManager {
constructor(storage = localStorage) {
this.storage = storage;
this.maxSize = this.estimateStorageLimit();
}
setItem(key, value, options = {}) {
const data = {
value: value,
timestamp: Date.now(),
priority: options.priority || 1,
expires: options.expires || null
};
const serialized = JSON.stringify(data);
if (!safeSetItem(key, serialized, this.storage)) {
-- Try cleanup and retry
this.cleanup();
return safeSetItem(key, serialized, this.storage);
}
return true;
}
getItem(key) {
const item = this.storage.getItem(key);
if (!item) return null;
try {
const data = JSON.parse(item);
-- Check expiration
if (data.expires && Date.now() > data.expires) {
this.storage.removeItem(key);
return null;
}
return data.value;
} catch (error) {
-- Invalid JSON, remove item
this.storage.removeItem(key);
return null;
}
}
cleanup() {
const items = [];
-- Collect all items with metadata
for (let i = 0; i < this.storage.length; i++) {
const key = this.storage.key(i);
const item = this.storage.getItem(key);
try {
const data = JSON.parse(item);
items.push({ key, data, size: item.length });
} catch (error) {
-- Remove invalid items
this.storage.removeItem(key);
}
}
-- Sort by priority and age (remove low priority, old items first)
items.sort((a, b) => {
if (a.data.priority !== b.data.priority) {
return a.data.priority - b.data.priority;
}
return a.data.timestamp - b.data.timestamp;
});
-- Remove items until we have some free space
const targetSize = this.getCurrentSize() * 0.8; -- Keep 80% full
let currentSize = this.getCurrentSize();
for (const item of items) {
if (currentSize <= targetSize) break;
this.storage.removeItem(item.key);
currentSize -= item.size;
}
}
getCurrentSize() {
let total = 0;
for (let i = 0; i < this.storage.length; i++) {
const key = this.storage.key(i);
const value = this.storage.getItem(key);
total += key.length + value.length;
}
return total;
}
estimateStorageLimit() {
-- Try to estimate storage limit
let estimate = 5 * 1024 * 1024; -- 5MB default
try {
const testKey = "__storage_test__";
const chunk = "0".repeat(1024); -- 1KB chunks
let size = 0;
while (size < 10 * 1024 * 1024) { -- Max 10MB test
this.storage.setItem(testKey, chunk.repeat(size / 1024));
size += 1024;
}
} catch (error) {
estimate = size;
} finally {
this.storage.removeItem("__storage_test__");
}
return estimate;
}
}
-- Usage example
const storageManager = new StorageManager();
-- Store with priority and expiration
storageManager.setItem("user_data", { name: "Alice" }, {
priority: 5,
expires: Date.now() + 24 * 60 * 60 * 1000 -- 24 hours
});
storageManager.setItem("cache_data", { data: "cached" }, {
priority: 1 -- Low priority, will be removed first
});
function notifyUserOfStorageLimit() {
console.warn("Storage limit reached. Some data may not be saved.");
-- Show user notification
}
Key Takeaways
Always wrap storage operations in try/catch blocks. Implement cleanup strategies for old data. Use expiration times and priority levels. Monitor storage usage and notify users when limits are reached.