{"record":{"id":"c0e71d9401c713aa","repo":"apache/dubbo","slug":"bytes2hex-offset-length-array-length","errorCode":null,"errorMessage":"bytes2hex: offset + length > array length.","messagePattern":"bytes2hex: offset \\+ length > array length\\.","errorType":"exception","errorClass":"IndexOutOfBoundsException","httpStatus":null,"severity":"error","filePath":"dubbo-common/src/main/java/org/apache/dubbo/common/io/Bytes.java","lineNumber":405,"sourceCode":"    }\n\n    /**\n     * to hex string.\n     *\n     * @param bs  byte array.\n     * @param off offset.\n     * @param len length.\n     * @return hex string.\n     */\n    public static String bytes2hex(byte[] bs, int off, int len) {\n        if (off < 0) {\n            throw new IndexOutOfBoundsException(\"bytes2hex: offset < 0, offset is \" + off);\n        }\n        if (len < 0) {\n            throw new IndexOutOfBoundsException(\"bytes2hex: length < 0, length is \" + len);\n        }\n        if (off + len > bs.length) {\n            throw new IndexOutOfBoundsException(\"bytes2hex: offset + length > array length.\");\n        }\n\n        byte b;\n        int r = off, w = 0;\n        char[] cs = new char[len * 2];\n        for (int i = 0; i < len; i++) {\n            b = bs[r++];\n            cs[w++] = BASE16[b >> 4 & MASK4];\n            cs[w++] = BASE16[b & MASK4];\n        }\n        return new String(cs);\n    }\n\n    /**\n     * from hex string.\n     *\n     * @param str hex string.\n     * @return byte array.","sourceCodeStart":387,"sourceCodeEnd":423,"githubUrl":"https://github.com/apache/dubbo/blob/3a3043227f5571d25eb2889de5bca22f2914843b/dubbo-common/src/main/java/org/apache/dubbo/common/io/Bytes.java#L387-L423","documentation":"Thrown by Bytes.bytes2hex(byte[], int, int) when off + len exceeds the byte array length. This is the upper-bound check ensuring the requested region [off, off+len) stays within the array. It runs after the offset and length sign checks, and prevents ArrayIndexOutOfBoundsException during the conversion loop.","triggerScenarios":"Calling Bytes.bytes2hex(bs, off, len) where off + len > bs.length. Stale array reference whose size shrank after the offset/length were computed; off-by-one where len includes one extra byte; passing array.length as offset with len > 0.","commonSituations":"Reusing a length computed against a larger buffer on a smaller sliced array; reading a length-prefixed field then truncating the buffer but keeping the old length; buffer reuse/recycling mismatch.","solutions":["Recompute or clamp len against the actual current array length: len = Math.min(len, bs.length - off).","Use bytes2hex(byte[]) for the full array to avoid manual region math.","Ensure the array reference and its computed region are derived from the same source state."],"exampleFix":"// before\nbyte[] slice = Arrays.copyOfRange(buf, 0, 10);\nString hex = Bytes.bytes2hex(slice, 0, buf.length); // throws [153]: buf.length > slice.length\n\n// after\nString hex = Bytes.bytes2hex(slice, 0, slice.length);","handlingStrategy":"validation","validationCode":"if (off + len > bs.length) len = bs.length - off; // clamp\nString hex = Bytes.bytes2hex(bs, off, len);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Recompute len against the actual array length after any buffer resize/truncation.","Use bytes2hex(byte[]) to avoid region arithmetic entirely.","Derive the array and its region from the same source state."],"tags":["bytes","hex","bounds-check","validation"],"backgroundTag":null,"analyzedSha":"3a3043227f5571d25eb2889de5bca22f2914843b","analyzedAt":"2026-08-14T00:43:19.853Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}