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.