apache/incubator-seata · error · IllegalArgumentException
Unknown TransactionExceptionCode[{ordinal}]
Error message
Unknown TransactionExceptionCode[{ordinal}] What it means
TransactionExceptionCode.get(int) maps a numeric code from the wire protocol back to the TransactionExceptionCode enum (e.g. BeginFailed, LockKeyConflict, IoErro). It throws when the ordinal is outside the enum range — either negative or >= values().length — i.e. the integer in the message header cannot be a valid exception code on this build.
Source
Thrown at core/src/main/java/org/apache/seata/core/exception/TransactionExceptionCode.java:170
* @param ordinal the ordinal
* @return the transaction exception code
*/
public static TransactionExceptionCode get(byte ordinal) {
return get((int) ordinal);
}
/**
* Get transaction exception code.
*
* @param ordinal the ordinal
* @return the transaction exception code
*/
public static TransactionExceptionCode get(int ordinal) {
TransactionExceptionCode value = null;
try {
value = TransactionExceptionCode.values()[ordinal];
} catch (Exception e) {
throw new IllegalArgumentException("Unknown TransactionExceptionCode[" + ordinal + "]");
}
return value;
}
}
View on GitHub (pinned to e01f97c6db)
Solutions
- Pin client and server to the same (or compatible) Seata version; upgrade the older side first during rolling upgrades.
- Verify only Seata nodes connect to the TC port; keep binary RPC traffic off HTTP proxies.
- Capture the frame (packet dump / debug logging of RpcMessage) to confirm whether the code field is genuinely newer or the payload is corrupt.
Example fix
# before client seata 1.5 <-> server seata 2.x (new codes) # after client upgraded to match server major.minor
Defensive patterns
Strategy: type-guard
Validate before calling
static boolean validTxExceptionCode(int ordinal) {
return ordinal >= 0 && ordinal < TransactionExceptionCode.values().length;
} Type guard
static Optional<TransactionExceptionCode> safeGet(int o) {
return (o >= 0 && o < TransactionExceptionCode.values().length)
? Optional.of(TransactionExceptionCode.values()[o]) : Optional.empty();
} Try / catch
catch (IllegalArgumentException e) {
if (e.getMessage().startsWith("Unknown TransactionExceptionCode")) {
LOG.warn("peer may run newer seata; code={}", ordinal);
// degrade: treat as generic TransactionException instead of killing the channel
}
} Prevention
- Gate connections on compatible version ranges during upgrades
- Log peer version at handshake to correlate later unknown-code errors
When it happens
Trigger: Decoding a TransactionExceptionCode from an RPC message sent by a peer with a different (usually newer) enum that has more codes, or from a corrupted/garbage frame where random bytes land in the code field.
Common situations: Version skew during rolling upgrades (newer TC sends a code the old client enum lacks), non-Seata traffic hitting the TC service port, or a proxy/load balancer corrupting the binary protocol.
Related errors
- Unknown ResultCode[{ordinal}]
- Unknown ClientType[{ordinal}]
- Unknown GlobalStatus[{code}]
- Unknown BranchType[{ordinal}]
- Invalid event format: expected prefix '{}', got: {}
AI-assisted analysis of apache/incubator-seata@e01f97c6db (2026-08-14).
Data as JSON: /api/errors/7039403a516d92e7.
Report an issue: GitHub.