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
- Ensure both sides use the same versions of the classes being serialized (Message and its payload types).
- Confirm the sender uses MessageEncoder/EncodeUtil.encodeBase64Zipped, not a different serialization format.
- Check the wrapped cause: ClassNotFoundException means add the missing jar; StreamCorruptedException means wrong bytes; ClassCastException means wrong object type.
- 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
- Keep identical versions of Message classes and their dependencies on producer and consumer.
- Pin serialVersionUID on all serializable Message classes.
- Never send non-Java-serialized payloads to an endpoint using MessageDecoder.
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
- Unable to read step info from XML node
- Unexpected error trying to decode object.
- Unexpected error trying to encode object.
- : Unknown storage type
- AbortMeta.Exception.UnexpectedErrorInReadingStepInfoFromRepo…
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)