TheAlgorithms/Java · error · IllegalArgumentException

Key and value must not be null

Error message

Key and value must not be null

What it means

Thrown by LIFOCache.put(K, V, long) when either the key or the value is null. The cache maintains both a HashMap and an insertion-order stack (keys), and a null value would be ambiguous with absent/expired entries while a null key would break both structures. The check runs before lock acquisition and before TTL validation.

Source

Thrown at src/main/java/com/thealgorithms/datastructures/caches/LIFOCache.java:172

        put(key, value, defaultTTL);
    }

    /**
     * Adds a key-value pair to the cache with a specified time-to-live (TTL).
     *
     * <p>If the key already exists, its value is removed, re-inserted at tail and its TTL is reset.
     * If the key does not exist and the cache is full, the youngest entry is evicted to make space.
     * Expired entries are also cleaned up prior to any eviction. The eviction listener
     * is notified when an entry gets evicted.
     *
     * @param key        the key to associate with the cached value; must not be {@code null}
     * @param value      the value to be cached; must not be {@code null}
     * @param ttlMillis  the time-to-live for this entry in milliseconds; must be >= 0
     * @throws IllegalArgumentException if {@code key} or {@code value} is {@code null}, or if {@code ttlMillis} is negative
     */
    public void put(K key, V value, long ttlMillis) {
        if (key == null || value == null) {
            throw new IllegalArgumentException("Key and value must not be null");
        }
        if (ttlMillis < 0) {
            throw new IllegalArgumentException("TTL must be >= 0");
        }

        lock.lock();
        try {
            // If key already exists, remove it. It will later be re-inserted at top of stack
            keys.remove(key);
            final CacheEntry<V> oldEntry = cache.remove(key);
            if (oldEntry != null && !oldEntry.isExpired()) {
                notifyEviction(key, oldEntry.value);
            }

            // Evict expired entries to make space for new entry
            evictExpired();

            // If no expired entry was removed, remove the youngest

View on GitHub (pinned to fdfb9a395b)

Solutions

  1. Skip caching when the value is null: if (value != null) cache.put(key, value, ttl);
  2. Represent absence with a sentinel or Optional.
  3. Validate key and value at the service boundary.

Example fix

// before
cache.put(id, repo.find(id), ttl);
// after
V value = repo.find(id);
if (id != null && value != null) cache.put(id, value, ttl);
Defensive patterns

Strategy: validation

Validate before calling

if (key == null || value == null) {
    return;
}
cache.put(key, value, ttlMillis);

Type guard

static <K, V> boolean isCacheable(K key, V value) {
    return key != null && value != null;
}

Prevention

When it happens

Trigger: cache.put(key, null, ttl); cache.put(null, value, ttl); caching a computation result that is null; DTO fields defaulting to null.

Common situations: Caching repository lookups that legitimately return absent; test fixtures missing a field; ORM mappings yielding null.

Related errors


AI-assisted analysis of TheAlgorithms/Java@fdfb9a395b (2026-08-13). Data as JSON: /api/errors/afadcffd6f7914db. Report an issue: GitHub.