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

  1. Check payload validity yourself before decoding: call StainTracePayload.isValid(payload) first.
  2. Verify the payload was produced by the same SeaTunnel version (format/header compatibility).
  3. Trace where the byte[] originated; re-generate or re-capture the trace payload if corrupted.
  4. 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

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


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)