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

  1. Remap keys: store key + 1 (or XOR/bias with a constant) so user key space never contains 0.
  2. Reserve 0 explicitly as an invalid sentinel in your key-issuing code and assert on it before calling at().
  3. 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

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


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/1ee88465cd2e93c4. Report an issue: GitHub.