apache/flink · error · UnsupportedOperationException

The end timestamp of a processing-time window cannot become

Error message

The end timestamp of a processing-time window cannot become earlier than the current processing time by merging. Current processing time: {processingTime} window: {mergeResult}

What it means

Thrown from the MergeFunction callback inside OneInputWindowProcessOperator while a record is added to a processing-time session window. After merging, the resulting window's maxTimestamp must still be later than the current processing time (mergeResult.maxTimestamp() > currentProcessingTime); a merged session that would already be due for trigger/cleanup is refused rather than created in an already-expired state. The message names the current processing time and the offending mergeResult window.

Source

Thrown at flink-datastream/src/main/java/org/apache/flink/datastream/impl/extension/window/operators/OneInputWindowProcessOperator.java:265

                                        if ((windowAssigner.isEventTime()
                                                && mergeResult.maxTimestamp() + allowedLateness
                                                        <= internalTimerService
                                                                .currentWatermark())) {
                                            throw new UnsupportedOperationException(
                                                    "The end timestamp of an "
                                                            + "event-time window cannot become earlier than the current watermark "
                                                            + "by merging. Current event time: "
                                                            + internalTimerService
                                                                    .currentWatermark()
                                                            + " window: "
                                                            + mergeResult);
                                        } else if (!windowAssigner.isEventTime()) {
                                            long currentProcessingTime =
                                                    internalTimerService.currentProcessingTime();
                                            if (mergeResult.maxTimestamp()
                                                    <= currentProcessingTime) {
                                                throw new UnsupportedOperationException(
                                                        "The end timestamp of a "
                                                                + "processing-time window cannot become earlier than the current processing time "
                                                                + "by merging. Current processing time: "
                                                                + currentProcessingTime
                                                                + " window: "
                                                                + mergeResult);
                                            }
                                        }

                                        triggerContext.setKey(key);
                                        triggerContext.setWindow(mergeResult);

                                        triggerContext.onMerge(mergedWindows);

                                        for (W m : mergedWindows) {
                                            triggerContext.setWindow(m);
                                            triggerContext.clear();
                                            WindowUtils.deleteCleanupTimer(

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Increase the session gap so gap > worst-case per-record delay (queueing + backpressure + GC) - the merged session then never expires before the merge completes.
  2. Fix the throughput problem: raise parallelism, reduce per-record work, check backpressure in the Web UI, enlarge network buffers.
  3. Prefer event-time session windows (WindowStrategy.session(gap, TimeType.EVENT)) when correctness must not depend on wall-clock pacing.
  4. In tests, drive processing-time windows with a controlled clock/pipelined input instead of sleeping between records.

Example fix

// before: gap smaller than queueing delay
stream.process(windowFn, WindowStrategy.session(Duration.ofSeconds(2), WindowStrategy.PROCESSING_TIME));
// after: gap dominates worst-case end-to-end delay
stream.process(windowFn, WindowStrategy.session(Duration.ofMinutes(1), WindowStrategy.PROCESSING_TIME));
Defensive patterns

Strategy: validation

Validate before calling

// fail fast at job setup: processing-time sessions need gap > worst-case record delay
long gapMs = sessionGap.toMillis();
if (gapMs <= measuredMaxRecordDelayMs) {
    throw new IllegalArgumentException(
            "processing-time session gap (" + gapMs + "ms) must exceed max record delay ("
                    + measuredMaxRecordDelayMs + "ms)");
}

Prevention

When it happens

Trigger: WindowStrategy.session(gap, TimeType.PROCESSING): a record is assigned a session that merges with existing sessions into a window whose end (assignment time + gap) is <= current processing time at merge time. Happens when records spend longer than the session gap in queues (backpressure, slow source, GC pause, checkpoint stall) so wall-clock time overtakes the merged window's end.

Common situations: Session gap of a few seconds under backpressure or checkpoint stalls; bursty catch-up sources (file/Kafka after idle); overloaded TaskManagers where processing time outruns throughput; tests that pre-generate element timestamps and then replay them slower than real time.

Related errors


AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14). Data as JSON: /api/errors/0e42b76bb19219dd. Report an issue: GitHub.