oracle/graal · error · IllegalArgumentException
Key in the prefix tree cannot be 0.
Error message
Key in the prefix tree cannot be 0.
What it means
SeqLockPrefixTree.Node.at(long key) uses 0 as the internal EMPTY_KEY sentinel marking absent slots in its lock-free child table, so 0 is not a valid user key. Passing key == 0 throws IllegalArgumentException before any lookup or child creation happens. This is a hard API contract of the prefix tree, not a sizing or concurrency issue.
Source
Thrown at sdk/src/org.graalvm.collections/src/org/graalvm/collections/SeqLockPrefixTree.java:157
* Set the value for the {@link LockFreePrefixTree.Node}.
*
* @param value the new value.
* @since 22.3
*/
public void setValue(long value) {
set(value);
}
/**
* Get existing (or create if missing) child with the given key.
*
* @param key the key of the child.
* @return The child with the given childKey.
* @since 22.3
*/
public Node at(long key) {
if (key == EMPTY_KEY) {
throw new IllegalArgumentException("Key in the prefix tree cannot be 0.");
}
Node child = findChildLockFree(key);
return child != null ? child : tryAddChild(key);
}
/**
* @return the value of the seqlock.
*
* @since 22.3
*/
public long seqlockValue() {
return seqlock;
}
private Node findChildLockFree(long key) {
final long seqlockStart = seqlock;
if ((seqlockStart & 1) == 1) {
// A modification is in progress.View on GitHub (pinned to a66e9ccd1d)
Solutions
- Remap keys: store key + 1 (or XOR/bias with a constant) so user key space never contains 0.
- Reserve 0 explicitly as an invalid sentinel in your key-issuing code and assert on it before calling at().
- If 0 must be representable, wrap the value in a different structure (e.g. a Node field) rather than as a child key.
Example fix
// before Node child = node.at(address); // address can be 0 // after Node child = node.at(address + 1); // biased keys: user 0 stored as 1
Defensive patterns
Strategy: validation
Validate before calling
static long toTreeKey(long userKey) {
if (userKey < 0) throw new IllegalArgumentException("userKey must be >= 0");
return userKey + 1; // bias so 0 never reaches the tree
}
// Node child = node.at(toTreeKey(userKey)); Prevention
- Treat 0 as reserved in any key space fed to SeqLockPrefixTree.
- Bias keys (+1) at the API edge of your wrapper.
- Assert computed keys != 0 in debug builds (addresses, hashes, indices).
When it happens
Trigger: Calling node.at(0), or node.at(x) where x is computed from IDs/offsets/compressed pointers that can legitimately evaluate to 0. Storing keys derived from addresses, hashes, or array indices where 0 means 'unset' elsewhere in the codebase.
Common situations: Using memory addresses or object identity hashes as tree keys where 0 denotes null/absent; porting code from a map that allowed 0; off-by-one index arithmetic producing 0 for the first element.
Related errors
- Interrupted while waiting for housekeeping thread shutdown.
- Allocator's housekeeping thread was interrupted.
- Invalid replay boolean flag
- Required replay field is null
- Unexpected architecture name ${archName}
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/1ee88465cd2e93c4.
Report an issue: GitHub.