apache/seatunnel · warning

Skip event with null or empty jobId

Error message

Skip event with null or empty jobId

What it means

The local-file trace writer skips StainTraceEvent records whose jobId is null or empty, logging this warning. Trace events are keyed by job for file organization, so an event without a job id cannot be written to a per-job trace file.

Solutions

  1. Ensure all StainTraceEvent instances are created with a valid jobId from the job execution context.
  2. Audit custom or upstream event sources that may emit events before jobId assignment.
  3. If events are legitimately pre-job, filter them at the producer instead of the handler.

Example fix

null
Defensive patterns

Strategy: validation

Validate before calling

if (event.getJobId() == null || event.getJobId().isEmpty()) { throw new IllegalArgumentException("StainTraceEvent requires a jobId"); }

Type guard

boolean hasJobId(StainTraceEvent e) { return e.getJobId() != null && !e.getJobId().isEmpty(); }

Prevention

When it happens

Trigger: writeEventsToFile() drains events from the ring buffer or local buffer and encounters a StainTraceEvent whose getJobId() returns null or "" — typically when an event was constructed without job context or deserialized incomplete.

Common situations: See trigger scenarios.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/1308920cdff66829. Report an issue: GitHub.

Appendix: source

Thrown at seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/event/JobEventLocalFileHandler.java:212

     * Converts buffered stain trace events into OTLP JSON lines and writes them to rotating files.
     */
    private boolean writeEventsToFile(
            Iterable<Event> events, boolean allowWriterCreateDuringClosing) throws IOException {
        // Guard before acquiring writerLock: if closing and this is not the authorized close-path
        // flush, bail out immediately.  A mid-loop return after partial writes would leave
        // committedEventIndex unchanged, causing close()'s re-read to duplicate those events.
        if (closing.get() && !allowWriterCreateDuringClosing) {
            return false;
        }
        synchronized (writerLock) {
            for (Event event : events) {
                if (!(event instanceof StainTraceEvent)) {
                    continue;
                }
                StainTraceEvent traceEvent = (StainTraceEvent) event;
                String jobId = traceEvent.getJobId();
                if (jobId == null || jobId.isEmpty()) {
                    log.warn("Skip event with null or empty jobId");
                    continue;
                }

                String otlpLine;
                try {
                    otlpLine = toOtlpJsonLine(traceEvent);
                } catch (Exception e) {
                    log.warn(
                            "Skip event with invalid payload for traceId={}: {}",
                            traceEvent.getTraceId(),
                            e.getMessage());
                    continue;
                }
                if (otlpLine == null) {
                    continue;
                }

                String date =

View on GitHub (pinned to cf67b549a7)