{"record":{"id":"cbc0aa3c94465852","repo":"oracle/graal","slug":"len-must-be-non-negative-but-was-d","errorCode":null,"errorMessage":"Len must be non negative but was %d","messagePattern":"Len must be non negative but was (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/truffle/BinaryInput.java","lineNumber":560,"sourceCode":"        }\n\n        ByteArrayBinaryInput(byte[] buffer, int length) {\n            super(length);\n            this.buffer = buffer;\n        }\n\n        @Override\n        public int read() {\n            if (pos >= length) {\n                return EOF;\n            }\n            return (buffer[pos++] & 0xff);\n        }\n\n        @Override\n        public void read(byte[] b, int off, int len) {\n            if (len < 0) {\n                throw new IllegalArgumentException(String.format(\"Len must be non negative but was %d\", len));\n            }\n            if (pos + len > length) {\n                throw new IndexOutOfBoundsException();\n            }\n            System.arraycopy(buffer, pos, b, off, len);\n            pos += len;\n        }\n\n        @Override\n        public ByteBuffer asByteBuffer(int len) {\n            ByteBuffer result = ByteBuffer.wrap(buffer, pos, len).slice().asReadOnlyBuffer();\n            pos += len;\n            return result;\n        }\n    }\n\n    private static final class CCharPointerInput extends BinaryInput {\n","sourceCodeStart":542,"sourceCodeEnd":578,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/truffle/BinaryInput.java#L542-L578","documentation":"The byte[]-backed BinaryInput implementation rejects negative lengths in read(byte[], int, int) with IllegalArgumentException('Len must be non negative but was %d') before any bounds check. It is a fail-fast precondition on the marshalling API, guarding the subsequent pos+len arithmetic and arraycopy.","triggerScenarios":"Calling read(b, off, len) (or an array-read helper that forwards to it, e.g. read(boolean[], int, int)) with a negative len value, often from a computed length like remaining()-alreadyRead.","commonSituations":"Length arithmetic bugs where a subtraction underflows (reading past a known count), reusing a read loop's len variable after it was decremented to negative, or an upstream protocol field carrying a negative count.","solutions":["Clamp or validate the computed length: len = Math.max(0, total - alreadyRead) before calling read.","Fix the loop condition that let len go negative (use while (remaining > 0)).","Validate protocol length fields at the boundary before translating them into read calls.","Add an assertion/log of (pos, len, length) at the call site to catch underflow early."],"exampleFix":"// before\nin.read(b, off, total - alreadyRead);\n// after\nint len = Math.max(0, total - alreadyRead);\nif (len > 0) in.read(b, off, len);","handlingStrategy":"validation","validationCode":"if (len < 0) throw new IllegalArgumentException(\"bad len: \" + len);\nin.read(b, off, len);","typeGuard":null,"tryCatchPattern":"try {\n    in.read(b, off, len);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Len must be non negative\")) {\n        // fix caller's length arithmetic; do not retry with the same len\n    }\n}","preventionTips":["Validate length fields from the protocol before translating to read calls","Guard computed lengths with Math.max(0, ...)","Loop with while (remaining > 0), not decremented counters that can underflow"],"tags":["libgraal","truffle","marshalling","precondition"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}