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

  1. Fix the slice arithmetic at the call site: valid ranges satisfy offset >= 0, byteCount >= 0, offset + byteCount <= buffer.length.
  2. In streaming loops, compute byteCount = Math.min(chunkSize, total - position) rather than reusing stale lengths.
  3. 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

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


AI-assisted analysis of Tencent/tinker@1b7ea02c23 (2026-08-14). Data as JSON: /api/errors/d5eeddeb95341107. Report an issue: GitHub.