pentaho/pentaho-kettle · error · RuntimeException

Unexpected error trying to decode object.

Error message

Unexpected error trying to decode object.

What it means

MessageDecoder.decode deserializes a byte array back into a Message via Java ObjectInputStream during WebSocket decoding. Any failure — invalid serialized stream, class not found on the classpath, wrong cast — is wrapped in this RuntimeException.

Solutions

  1. Ensure both sides use the same versions of the classes being serialized (Message and its payload types).
  2. Confirm the sender uses MessageEncoder/EncodeUtil.encodeBase64Zipped, not a different serialization format.
  3. Check the wrapped cause: ClassNotFoundException means add the missing jar; StreamCorruptedException means wrong bytes; ClassCastException means wrong object type.
  4. Verify serialVersionUids match if custom classes were changed between versions.

Example fix

// before
Object o = ois.readObject(); // ClassNotFoundException wrapped at runtime
// after
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data))) {
  Object o = ois.readObject();
  if (!(o instanceof Message)) {
    throw new IllegalArgumentException("Unexpected payload type: " + o.getClass());
  }
  return (Message) o;
} catch (ClassNotFoundException e) {
  throw new IllegalStateException("Message class missing on decoder classpath: " + e.getMessage(), e);
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (data == null || data.length == 0) throw new IllegalArgumentException("no bytes to decode");

Type guard

// after decode, narrow before use
if (obj instanceof Message) { Message m = (Message) obj; }

Try / catch

try {
  Message m = decoder.decode(data);
} catch (RuntimeException e) {
  Throwable cause = e.getCause();
  if (cause instanceof ClassNotFoundException) {
    logger.error("Missing class on decoder side: " + cause.getMessage());
  }
  throw e;
}

Prevention

When it happens

Trigger: A WebSocket text/binary message whose decoded bytes are not a Java-serialized Message; the serialized class is missing from the receiving side's classpath (ClassNotFoundException); the deserialized object is not a Message (ClassCastException).

Common situations: Producer and consumer running different Pentaho/Kettle versions with incompatible serialized classes; sending non-Java-serialized data (e.g. plain JSON) to an endpoint expecting serialized Message objects; missing engine-ext jar on the decoder side.

Related errors


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

Appendix: source

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

 */
public class MessageDecoder implements Decoder.Text<Message> {
  /**
   * Decode the given String into an AEL Message object.
   *
   * @param string string to be decoded.
   * @return the decoded message as an Message object.
   */
  @Override
  public Message decode( String string ) throws DecodeException {
    try {
      byte[] data = EncodeUtil.decodeBase64Zipped( string );
      InputStream is = new ByteArrayInputStream( data );
      ObjectInputStream ois = new ObjectInputStream( is );
      Object o = ois.readObject();
      ois.close();
      return (Message) o;
    } catch ( Exception e ) {
      throw new RuntimeException( "Unexpected error trying to decode object.", e );
    }
  }

  @Override
  public boolean willDecode( String s ) {
    return true;
  }

  @Override
  public void init( EndpointConfig config ) {
    // Do Nothing
  }

  @Override
  public void destroy() {
    // Do Nothing
  }

View on GitHub (pinned to f3058517a1)