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 in TwoInputNonBroadcastWindowProcessOperator while a left-input record is added to a processing-time session window. The merged window must satisfy mergeResult.maxTimestamp() > currentProcessingTime; a merged session already at or past the current wall clock is refused with UnsupportedOperationException because it would be born expired. 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/TwoInputNonBroadcastWindowProcessOperator.java:302

                                        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 beyond the worst-case queueing/backpressure delay observed for both inputs.
  2. Diagnose and fix backpressure (Web UI backpressure tab, raise parallelism, reduce per-record work, check the slower input).
  3. Switch to event-time session windows so merge validity depends on watermarks, not wall-clock pacing.
  4. In tests, pump both inputs without artificial wall-clock delays, or use a controlled processing-time service.

Example fix

// before
... .process(twoInputFn, WindowStrategy.session(Duration.ofSeconds(2), WindowStrategy.PROCESSING_TIME));
// after: gap dominates worst-case delay, or move to event time
... .process(twoInputFn, WindowStrategy.session(Duration.ofMinutes(1), WindowStrategy.PROCESSING_TIME));
Defensive patterns

Strategy: validation

Validate before calling

// setup-time assertion: gap must exceed worst-case delay of the slower input
if (sessionGap.toMillis() <= maxObservedDelayMs) {
    throw new IllegalArgumentException(
            "session gap too small for observed delay " + maxObservedDelayMs + "ms");
}

Prevention

When it happens

Trigger: WindowStrategy.session(gap, TimeType.PROCESSING) on a two-input job: a left-input record merges sessions into a window whose end (arrival time + gap) is <= current processing time, which occurs when the record spent longer than the session gap in queues (backpressure, slow source, GC) before reaching the operator.

Common situations: Two-input jobs where one input is slow and backpressures the operator; small session gaps; checkpoint stalls; bursty catch-up after idle periods; overloaded TaskManagers.

Related errors


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