{"record":{"id":"1ee88465cd2e93c4","repo":"oracle/graal","slug":"key-in-the-prefix-tree-cannot-be-0","errorCode":null,"errorMessage":"Key in the prefix tree cannot be 0.","messagePattern":"Key in the prefix tree cannot be 0\\.","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"sdk/src/org.graalvm.collections/src/org/graalvm/collections/SeqLockPrefixTree.java","lineNumber":157,"sourceCode":"         * Set the value for the {@link LockFreePrefixTree.Node}.\n         *\n         * @param value the new value.\n         * @since 22.3\n         */\n        public void setValue(long value) {\n            set(value);\n        }\n\n        /**\n         * Get existing (or create if missing) child with the given key.\n         *\n         * @param key the key of the child.\n         * @return The child with the given childKey.\n         * @since 22.3\n         */\n        public Node at(long key) {\n            if (key == EMPTY_KEY) {\n                throw new IllegalArgumentException(\"Key in the prefix tree cannot be 0.\");\n            }\n            Node child = findChildLockFree(key);\n            return child != null ? child : tryAddChild(key);\n        }\n\n        /**\n         * @return the value of the seqlock.\n         *\n         * @since 22.3\n         */\n        public long seqlockValue() {\n            return seqlock;\n        }\n\n        private Node findChildLockFree(long key) {\n            final long seqlockStart = seqlock;\n            if ((seqlockStart & 1) == 1) {\n                // A modification is in progress.","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/sdk/src/org.graalvm.collections/src/org/graalvm/collections/SeqLockPrefixTree.java#L139-L175","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nNode child = node.at(address); // address can be 0\n\n// after\nNode child = node.at(address + 1); // biased keys: user 0 stored as 1","handlingStrategy":"validation","validationCode":"static long toTreeKey(long userKey) {\n    if (userKey < 0) throw new IllegalArgumentException(\"userKey must be >= 0\");\n    return userKey + 1; // bias so 0 never reaches the tree\n}\n// Node child = node.at(toTreeKey(userKey));","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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)."],"tags":["graalvm","collections","prefix-tree","validation"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}