apereo/cas · error · IOException
Deserialization error
Error message
Deserialization error
What it means
EncryptedTranscoder.decode wraps all failures during decryption, decompression, and ObjectInputStream.readObject into a generic IOException("Deserialization error"). CAS uses this transcoder to decrypt/deserialize the webflow session state carried in the execution parameter, so any corruption, wrong cipher key, or incompatible serialized class fails here.
Solutions
- Ensure all CAS nodes share identical cas.webflow.crypto encryption/signing key and secret values
- Force the user's flow to restart (clear the webflow session/cookie) since old keys cannot recover old state
- Verify compression setting matches how the payload was written by the transcoder's counterpart encode()
- Check for serialVersionUID/classpath changes after an upgrade and redeploy consistent artifacts server-side
- Enable debug logging of the underlying cause (already logged via LoggingUtils) to identify cipher vs stream failure
Example fix
// before (cluster node with wrong key) cas.webflow.crypto.encryption.key=OLD_KEY // after cas.webflow.crypto.encryption.key=<same key on every node>
Defensive patterns
Strategy: try-catch
Validate before calling
// before decode
if (bytes == null || bytes.length == 0) throw new IllegalArgumentException("Empty webflow state"); Try / catch
try {
Object state = transcoder.decode(bytes);
} catch (IOException e) {
logger.warn("Webflow state unusable (key mismatch or corrupt); restarting flow", e);
// discard execution key and restart the login flow
} Prevention
- Keep cas.webflow.crypto keys identical across all cluster nodes
- Restart the flow rather than retrying decode of stale state
- After CAS upgrades, expect old flow executions to fail and force re-login
- Verify compression settings symmetric between encode/decode
When it happens
Trigger: Calling decode() on a byte buffer whose ciphertext was encrypted with a different cas.webflow crypto key/secret, or whose payload is not GZIP-compressed while this.compression is true, or whose serialized classes are missing/changed (InvalidClassException, ClassNotFoundException), or truncated input.
Common situations: Rotating or mismatching cas.webflow.crypto.encryption.key/signing.key across nodes in a cluster; upgrading CAS so serialized flow-session classes changed serialVersionUID; load balancer sending the flowExecutionKey to a server with different crypto config; cookie/storage truncation.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- State [ : : ] does not have a matching transition for
- Unable to locate authentication object in the webflow…
- No authentication found for ticket
- Unable to determine authentication from the request context
- Unknown Duo Security authentication attempt
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/76bc9abf14a764ac.
Report an issue: GitHub.
Appendix: source
Thrown at core/cas-server-core-webflow-api/src/main/java/org/apereo/cas/web/flow/executor/EncryptedTranscoder.java:70
LoggingUtils.warn(LOGGER, e);
}
return encrypt(outBuffer);
}
}
@Override
@SuppressWarnings("BanSerializableRead")
public Object decode(final byte[] encoded) throws IOException {
val data = decrypt(encoded);
try (val inBuffer = new ByteArrayInputStream(data);
val in = this.compression
? new ObjectInputStream(new GZIPInputStream(inBuffer))
: new ObjectInputStream(inBuffer)) {
return in.readObject();
} catch (final Exception e) {
LoggingUtils.error(LOGGER, e);
throw new IOException("Deserialization error", e);
}
}
@SuppressWarnings("BanSerializableRead")
protected void writeObjectToOutputStream(final Object o, final ObjectOutputStream out) throws IOException {
var object = o;
if (AopUtils.isAopProxy(o)) {
try {
object = ((Advised) o).getTargetSource().getTarget();
} catch (final Exception e) {
LoggingUtils.error(LOGGER, e);
}
if (object == null) {
LOGGER.error("Could not determine object [{}] from proxy",
Objects.requireNonNull(o).getClass().getSimpleName());
}
}
if (object != null) {View on GitHub (pinned to e7288fc434)