apache/seatunnel · warning · IllegalArgumentException
Invalid stain trace payload
Error message
Invalid stain trace payload
What it means
StainTracePayload.readTraceId() extracts the 8-byte big-endian trace id from a stain-trace payload byte array. It first validates the payload via isValid() (null check, minimum header length, and magic/version checks); a payload failing validation throws IllegalArgumentException with this message.
Solutions
- Check payload validity yourself before decoding: call StainTracePayload.isValid(payload) first.
- Verify the payload was produced by the same SeaTunnel version (format/header compatibility).
- Trace where the byte[] originated; re-generate or re-capture the trace payload if corrupted.
- If hand-building payloads, use StainTracePayload's write/serialize API instead of manual ByteBuffer code.
Example fix
// before: decode without checking
long id = StainTracePayload.readTraceId(bytes);
// after: validate first
if (StainTracePayload.isValid(bytes)) {
long id = StainTracePayload.readTraceId(bytes);
} else {
log.warn("skip invalid stain trace payload, len=" + (bytes == null ? -1 : bytes.length));
} Defensive patterns
Strategy: type-guard
Validate before calling
if (payload == null || payload.length < StainTracePayload.HEADER_LENGTH || !StainTracePayload.isValid(payload)) { /* skip or regenerate */ } Type guard
boolean hasValidTracePayload(byte[] payload) { return payload != null && payload.length >= StainTracePayload.HEADER_LENGTH && StainTracePayload.isValid(payload); } Try / catch
try { long id = StainTracePayload.readTraceId(payload); } catch (IllegalArgumentException e) { if (e.getMessage().equals("Invalid stain trace payload")) { log.warn("bad trace payload, dropping"); } else { throw e; } } Prevention
- Always call isValid() before readTraceId()
- Keep SeaTunnel versions consistent across nodes writing/reading payloads
- Use the official serialize API rather than hand-crafted byte arrays
When it happens
Trigger: readTraceId(payload) called with a null payload, a payload shorter than HEADER_LENGTH, or a payload whose header bytes (magic/format marker) do not match the expected stain-trace format.
Common situations: Corrupted or truncated trace payload in transit or persisted state; cross-version incompatibility where an older node writes a different payload format; custom code hand-crafting the byte array incorrectly (wrong endianness or missing header).
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Skip event with invalid payload for traceId=
- Already an MDCCallable
- Already an MDCComparator
- Already an MDCConsumer
- Already an MDCExecutor
AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10).
Data as JSON: /api/errors/d894aef55fb8a665.
Report an issue: GitHub.
Appendix: source
Thrown at seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/trace/StainTracePayload.java:55
private static final int START_TS_OFFSET = TRACE_ID_OFFSET + 8;
private static final int COUNT_OFFSET = START_TS_OFFSET + 8;
/**
* Creates a new payload header for a freshly sampled row before any stage entries are appended.
*/
public static byte[] init(long traceId, long startTsMs) {
ByteBuffer buffer = ByteBuffer.allocate(HEADER_LENGTH).order(ByteOrder.BIG_ENDIAN);
buffer.putInt(MAGIC);
buffer.putShort(VERSION);
buffer.putLong(traceId);
buffer.putLong(startTsMs);
buffer.putShort((short) 0);
return buffer.array();
}
public static long readTraceId(byte[] payload) {
if (!isValid(payload)) {
throw new IllegalArgumentException("Invalid stain trace payload");
}
return ByteBuffer.wrap(payload).order(ByteOrder.BIG_ENDIAN).getLong(TRACE_ID_OFFSET);
}
public static boolean isValid(byte[] payload) {
if (payload == null || payload.length < HEADER_LENGTH) {
return false;
}
ByteBuffer buffer = ByteBuffer.wrap(payload).order(ByteOrder.BIG_ENDIAN);
int magic = buffer.getInt(0);
if (magic != MAGIC) {
return false;
}
short ver = buffer.getShort(4);
if (ver != VERSION) {
return false;
}
int count = readUnsignedShort(payload, COUNT_OFFSET);View on GitHub (pinned to cf67b549a7)