ruby-concurrency/concurrent-ruby · error · NullPointerException

NullPointerException

Error message

NullPointerException

What it means

This NullPointerException comes from internalPutAll, the bulk-copy engine behind putAll(Map) and the copy constructor ConcurrentHashMapV8(Map). While copying entries one by one it rejects a null entry, a null key, or a null value in the SOURCE map: it sets an npe flag, stops the copy, reconciles the size counter in a finally block, and only then throws so the table is never left locked or miscounted. The copy is not atomic - every entry visited before the offending one has already been inserted, so the target map is left partially populated.

Source

Thrown at ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java:2038

                            }
                        }
                        if (count != 0) {
                            if (count > 1) {
                                counter.add(delta);
                                delta = 0L;
                                checkForResize();
                            }
                            break;
                        }
                    }
                }
            }
        } finally {
            if (delta != 0)
                counter.add(delta);
        }
        if (npe)
            throw new NullPointerException();
    }

    /* ---------------- Table Initialization and Resizing -------------- */

    /**
     * Returns a power of two table size for the given desired capacity.
     * See Hackers Delight, sec 3.2
     */
    private static final int tableSizeFor(int c) {
        int n = c - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }

View on GitHub (pinned to 0b88d5ff75)

Solutions

  1. Sanitize the source map before copying: drop or substitute entries whose key or value is null
  2. Copy entry by entry with explicit null checks so you control which entry fails and can log it
  3. If null values are meaningful, store a sentinel object (or an Optional wrapper) instead of null
  4. On JRuby, reject nil keys/values upstream before any bulk insert into Concurrent::Map

Example fix

// before
Map<String,String> src = readConfig(); // may contain null values
chm.putAll(src); // NullPointerException, chm partially populated

// after
for (Map.Entry<String,String> e : src.entrySet()) {
    if (e.getKey() != null && e.getValue() != null)
        chm.put(e.getKey(), e.getValue());
}
Defensive patterns

Strategy: validation

Validate before calling

boolean copyable(Map<?,?> m) {
    for (Map.Entry<?,?> e : m.entrySet())
        if (e == null || e.getKey() == null || e.getValue() == null) return false;
    return true;
}
// use: if (copyable(src)) chm.putAll(src); else copyWithChecks(src);

Try / catch

try { chm.putAll(src); } catch (NullPointerException e) { // src held a null entry; chm is PARTIALLY populated - clear and rebuild before retrying }

Prevention

When it happens

Trigger: chm.putAll(source) where source is a HashMap or TreeMap that permits null keys or values; new ConcurrentHashMapV8<K,V>(m) copy construction when m contains a null entry; on JRuby, copying a Ruby Hash with nil keys or nil values into a Concurrent::Map backed by this Java class.

Common situations: Migrating data from java.util.HashMap or TreeMap (both tolerate null values) into a null-hostile concurrent map; bulk-loading JSON-parsed or config-derived maps where absent values became null; the same Ruby code storing nils works on MRI (Ruby Hash allows nil) but raises on JRuby where this extension backs Concurrent::Map.

Related errors


AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21). Data as JSON: /api/errors/169e12a7021e053a. Report an issue: GitHub.