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 TwoOutputWindowProcessOperator while a record is added to a processing-time session window. The merged window must end strictly after the current processing time; a session that would already be at/past wall clock when created is refused with UnsupportedOperationException instead of being born expired.

Source

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

                                        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. Raise the session gap above the worst-case observed per-record delay.
  2. Eliminate the backpressure/stall source (parallelism, per-record cost, GC tuning).
  3. Move to event-time sessions when wall-clock pacing is unacceptable.
  4. Load-test before deploying processing-time sessions.

Example fix

// before
stream.process(twoOutputFn, WindowStrategy.session(Duration.ofSeconds(2), WindowStrategy.PROCESSING_TIME));
// after
stream.process(twoOutputFn, WindowStrategy.session(Duration.ofMinutes(1), WindowStrategy.PROCESSING_TIME));
Defensive patterns

Strategy: validation

Validate before calling

// setup check: processing-time session gap vs worst-case record delay
if (sessionGap.toMillis() <= maxRecordDelayMs) {
    throw new IllegalArgumentException(
            "session gap (" + sessionGap + ") must exceed max record delay (" + maxRecordDelayMs + "ms)");
}

Prevention

When it happens

Trigger: WindowStrategy.session(gap, TimeType.PROCESSING): a record delayed longer than the session gap (backpressure, slow source, GC/checkpoint stall) merges sessions into a window whose maxTimestamp is <= current processing time at merge time.

Common situations: Small gaps under load; overloaded TaskManagers; bursty catch-up sources; tests replaying pre-timestamped records slower than wall clock.

Related errors


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