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
- Sanitize the source map before copying: drop or substitute entries whose key or value is null
- Copy entry by entry with explicit null checks so you control which entry fails and can log it
- If null values are meaningful, store a sentinel object (or an Optional wrapper) instead of null
- 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
- Never putAll an unsanitized java.util map that permits nulls
- Remember the copy is partial on failure - verify or rebuild state after catching
- Keep null out of producer maps at the boundary
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
- IllegalStateException
- Could not initialize intrinsics
- Could not initialize intrinsics
- Required array size too large
- cannot enqueue nil
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/169e12a7021e053a.
Report an issue: GitHub.