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

  1. Size the destination as (source.length / 4) * 3 for the full decode, or ensure at least 3 bytes remain from destOffset per call.
  2. Clamp per-iteration writes to the remaining output capacity.
  3. 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

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


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)