pentaho/pentaho-kettle · error · RuntimeException

Unexpected error trying to decode object.

Error message

Unexpected error trying to decode object.

What it means

EncodeUtil.decodeBase64Zipped decodes a Base64 string, inflates it with GZIP, and returns the raw serialized bytes. Any failure during Base64 decoding, GZIP inflation, or stream reading is wrapped in this RuntimeException because the utility has no checked-exception contract.

Solutions

  1. Verify the payload was produced by the matching EncodeUtil.encodeBase64Zipped and passed through unmodified.
  2. Check whether a proxy/gateway or WebSocket frame limit truncates or re-encodes the payload.
  3. Decode manually with Base64.getDecoder() and a GZIPInputStream to see which stage fails and get a clearer error.
  4. Log the first/last few bytes of the Base64 string to confirm corruption vs. wrong encoding.

Example fix

// before
byte[] data = EncodeUtil.decodeBase64Zipped(untrustedString);
// after
String b64 = untrustedString.trim();
if (!b64.isEmpty() && java.util.Arrays.equals(
    java.util.Base64.getDecoder().decode(b64.substring(0, Math.min(2, b64.length() * 4 / 3 + 4))),
    new byte[]{(byte)0x1f, (byte)0x8b})) {
  byte[] data = EncodeUtil.decodeBase64Zipped(b64);
} else {
  throw new IllegalArgumentException("payload is not gzip/base64 from encodeBase64Zipped");
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (payload == null || payload.trim().isEmpty()) throw new IllegalArgumentException("empty base64 payload");
byte[] raw = java.util.Base64.getDecoder().decode(payload.trim()); // throws early on bad base64
if (raw.length < 2 || raw[0] != 0x1f || raw[1] != (byte)0x8b) throw new IllegalArgumentException("payload is not gzip data");

Try / catch

try {
  byte[] data = EncodeUtil.decodeBase64Zipped(payload);
} catch (RuntimeException e) {
  logger.error("decodeBase64Zipped failed: " + e.getCause(), e);
  throw new IllegalArgumentException("Corrupt or non-gzip base64 payload", e);
}

Prevention

When it happens

Trigger: Calling EncodeUtil.decodeBase64Zipped(String) with a string that is not valid Base64, is not GZIP-compressed (missing 0x1f8b magic header), or is truncated/corrupted in transit.

Common situations: WebSocket message payloads mangled by an intermediary or proxy; decoding data that was produced without GZIP compression; truncated Base64 from cutting a payload at a length limit; copy-paste dropping trailing characters.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13). Data as JSON: /api/errors/4bd4c26c399e6066. Report an issue: GitHub.

Appendix: source

Thrown at engine-ext/api/src/main/java/org/pentaho/di/engine/api/remote/EncodeUtil.java:60

    if ( string == null || string.isEmpty() ) {
      return new byte[0];
    }

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    // base 64 decode
    byte[] bytes64 = org.apache.commons.codec.binary.Base64.decodeBase64( string.getBytes(  ) );
    ByteArrayInputStream zip = new ByteArrayInputStream( bytes64 );

    try (
      GZIPInputStream unzip = new GZIPInputStream( zip, ZIP_BUFFER_SIZE );
      BufferedInputStream in = new BufferedInputStream( unzip, ZIP_BUFFER_SIZE );
    ) {
      byte[] buff = new byte[ ZIP_BUFFER_SIZE ];
      for ( int length = 0; ( length = in.read( buff ) ) > 0; ) {
        baos.write( buff, 0, length );
      }
    } catch ( Exception e ) {
      throw new RuntimeException( "Unexpected error trying to decode object.", e );
    }

    return baos.toByteArray();
  }

  /**
   * Base64 encodes then zips a byte array into a compressed string
   *
   * @param src the source byte array
   * @return a compressed, base64 encoded string
   * @throws IOException
   */
  public static String encodeBase64Zipped( byte[] src ) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream( 1024 );
    try ( Base64OutputStream base64OutputStream = new Base64OutputStream( baos );
          GZIPOutputStream gzos = new GZIPOutputStream( base64OutputStream ) ) {
      gzos.write( src );
    }

View on GitHub (pinned to f3058517a1)