Tencent/tinker · error · ArrayIndexOutOfBoundsException
length=${arrayLength}; regionStart=${offset}; regionLength=$
Error message
length=${arrayLength}; regionStart=${offset}; regionLength=${count} What it means
checkOffsetAndCount is a bounds guard for (buffer, offset, byteCount) triples, mirroring Arrays.checkOffsetAndCount: it rejects negative offset/count, offset beyond arrayLength, or offset+count exceeding arrayLength, throwing ArrayIndexOutOfBoundsException with a diagnostic message showing all three values. It exists to give a precise message instead of the JVM's generic AIOOBE when write() is called with a bad slice.
Source
Thrown at third-party/tinker-ziputils/src/main/java/com/tencent/tinker/ziputils/ziputil/AlignedZipOutputStream.java:474
@Override
public void write(byte[] buffer, int offset, int byteCount) throws IOException {
checkOffsetAndCount(buffer.length, offset, byteCount);
if (currentEntry == null) {
throw new ZipException("No active entry");
}
if (currentEntry.getMethod() == STORED) {
out.write(buffer, offset, byteCount);
} else {
super.write(buffer, offset, byteCount);
}
crc.update(buffer, offset, byteCount);
crcDataSize += byteCount;
}
private void checkOffsetAndCount(int arrayLength, int offset, int count) {
if ((offset | count) < 0 || offset > arrayLength || arrayLength - offset < count) {
throw new ArrayIndexOutOfBoundsException("length=" + arrayLength + "; regionStart=" + offset
+ "; regionLength=" + count);
}
}
private void checkOpen() throws IOException {
if (closed) {
throw new IOException("Stream is closed");
}
}
}
View on GitHub (pinned to 1b7ea02c23)
Solutions
- Fix the slice arithmetic at the call site: valid ranges satisfy offset >= 0, byteCount >= 0, offset + byteCount <= buffer.length.
- In streaming loops, compute byteCount = Math.min(chunkSize, total - position) rather than reusing stale lengths.
- Add an assertion/log of (buffer.length, offset, byteCount) before write while debugging to see which term drifts.
Example fix
// before: wrong slice length passed to write zos.write(buf, off, buf.length); // throws when off > 0 // after: pass the remaining region, not the full array length zos.write(buf, off, buf.length - off);
Defensive patterns
Strategy: validation
Validate before calling
// Validate the (buffer, offset, count) triple before writing
static boolean validRegion(byte[] buf, int off, int count) {
return off >= 0 && count >= 0 && off <= buf.length && buf.length - off >= count;
}
if (!validRegion(buffer, offset, byteCount)) throw new IndexOutOfBoundsException("bad write slice");
zos.write(buffer, offset, byteCount); Try / catch
try {
zos.write(buffer, offset, byteCount);
} catch (ArrayIndexOutOfBoundsException e) {
throw new IllegalStateException("bad slice: buf.length=" + buffer.length
+ " offset=" + offset + " count=" + byteCount, e);
} Prevention
- In streaming loops compute count = Math.min(chunk, remaining) fresh each iteration.
- Never pass buffer.length as count when offset is non-zero — use buffer.length - offset.
- Prefer ByteBuffer.slice() to hand-rolled offset arithmetic.
When it happens
Trigger: Calling AlignedZipOutputStream.write(byte[] buffer, int offset, int byteCount) (or an internal path that funnels through this check) with offset/count that do not describe a valid region of buffer — e.g. offset == buffer.length with byteCount > 0, or negative byteCount from an upstream length computation.
Common situations: Streaming loops that compute remaining lengths incorrectly (int underflow near end of stream); passing (buffer, start, buffer.length) instead of (buffer, start, buffer.length - start); reused buffer pools where the working length is tracked separately from the array length and drifts.
Related errors
- Expected ${DEX_IN_JAR_NAME} in ${file}
- CRC mismatch
- Size mismatch
- Entry already exists: {}
- Too many entries for the zip file format's 16-bit entry coun
AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14).
Data as JSON: /api/errors/d5eeddeb95341107.
Report an issue: GitHub.