TooTallNate/Java-WebSocket · error · IllegalArgumentException
Destination array with length
Error message
Destination array with length %d cannot have offset of %d and still store three bytes.
What it means
The mirror check on the output side: decode4to3 needs room to write 3 bytes at destOffset. If destOffset is negative or destOffset + 2 >= destination.length, it throws IllegalArgumentException with the destination length and offset. This prevents silent buffer overflow during decoding.
Solutions
- Size the destination as (source.length / 4) * 3 for the full decode, or ensure at least 3 bytes remain from destOffset per call.
- Clamp per-iteration writes to the remaining output capacity.
- Prefer the single-shot Base64.decode API over manual buffer management.
Example fix
// before byte[] dest = new byte[src.length / 3]; Base64.decode(src, 0, src.length, dest, 0); // after byte[] dest = new byte[src.length / 4 * 3]; Base64.decode(src, 0, src.length, dest, 0);
Defensive patterns
Strategy: validation
Validate before calling
int outLen = src.length / 4 * 3;
if (dest == null || destOffset < 0 || destOffset + 3 > dest.length) {
dest = new byte[outLen];
destOffset = 0;
} Type guard
static boolean canWriteThreeBytes(byte[] a, int off) {
return a != null && off >= 0 && off + 3 <= a.length;
} Try / catch
try {
Base64.decode(src, 0, src.length, dest, 0);
} catch (IllegalArgumentException e) {
dest = new byte[src.length / 4 * 3];
Base64.decode(src, 0, src.length, dest, 0);
} Prevention
- Size decode output buffers as (input.length / 4) * 3.
- Track remaining output capacity when decoding chunks into a shared buffer.
- Let the library allocate: use Base64.decode(...) returning a new array.
When it happens
Trigger: A decode call supplying an output buffer/offset where fewer than 3 bytes remain after destOffset — e.g. destination sized as src.length/3 instead of src.length/4*3, or an offset into a small scratch buffer.
Common situations: Miscomputing decoded size (encoded length * 3/4 vs /4*3); reusing a too-small pooled buffer; decoding chunks into a fixed-size output array without bounds tracking.
Related errors
- Cannot have offset of
- Source array with length
- Cannot have length offset:
- Source array was null.
- Destination array was null.
AI-assisted analysis of TooTallNate/Java-WebSocket@afeacbf8c0 (2026-09-09).
Data as JSON: /api/errors/7017c012572fe1c5.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/org/java_websocket/util/Base64.java:819
*/
private static int decode4to3(
byte[] source, int srcOffset,
byte[] destination, int destOffset, int options) {
// Lots of error checking and exception throwing
if (source == null) {
throw new IllegalArgumentException("Source array was null.");
} // end if
if (destination == null) {
throw new IllegalArgumentException("Destination array was null.");
} // end if
if (srcOffset < 0 || srcOffset + 3 >= source.length) {
throw new IllegalArgumentException(String.format(
"Source array with length %d cannot have offset of %d and still process four bytes.",
source.length, srcOffset));
} // end if
if (destOffset < 0 || destOffset + 2 >= destination.length) {
throw new IllegalArgumentException(String.format(
"Destination array with length %d cannot have offset of %d and still store three bytes.",
destination.length, destOffset));
} // end if
final byte[] DECODABET = getDecodabet(options);
// Example: Dk==
if (source[srcOffset + 2] == EQUALS_SIGN) {
// Two ways to do the same thing. Don't know which way I like best.
//int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 )
// | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 );
int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18)
| ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12);
destination[destOffset] = (byte) (outBuff >>> 16);
return 1;
}
View on GitHub (pinned to afeacbf8c0)