apache/beam · error · IllegalStateException

Two accumulators contain different initial sequences

Error message

Two accumulators contain different initial sequences: %s and %s

What it means

SequenceRangeAccumulator.merge() combines two accumulators during Beam combine. If both have non-null but different initialSequence values, merging would silently blend events from different streams, so it throws IllegalStateException. This protects the semantic that all events in one accumulator share the same starting sequence.

Solutions

  1. Verify the input PCollection is keyed such that only events sharing one initial sequence reach the same accumulator.
  2. Fix fanout/hot-key logic to maintain key separation before combine.
  3. If legitimately merging independent streams, renumber sequences upstream so they share a consistent initial sequence.
  4. Review custom mergeAccumulators overrides to ensure they only merge same-key accumulators.

Example fix

// before
accA.merge(accB); // initialSequence 1 vs 9 -> IllegalStateException
// after
if (accA.getInitialSequence().equals(accB.getInitialSequence())) {
  accA.merge(accB);
} else {
  throw new IllegalArgumentException("cannot merge different streams");
}
Defensive patterns

Strategy: validation

Validate before calling

if (a.getInitialSequence() != null && b.getInitialSequence() != null
    && !a.getInitialSequence().equals(b.getInitialSequence())) {
  throw new IllegalArgumentException("refusing to merge accumulators from different streams");
}

Type guard

static boolean mergeable(SequenceRangeAccumulator a, SequenceRangeAccumulator b) {
  return a.getInitialSequence() == null || b.getInitialSequence() == null
      || a.getInitialSequence().equals(b.getInitialSequence());
}

Try / catch

try {
  acc.mergeAccumulators(other);
} catch (IllegalStateException e) {
  if (e.getMessage().startsWith("Two accumulators contain different initial sequences")) {
    throw new IllegalStateException("check keying/fanout: different streams combined", e);
  }
  throw e;
}

Prevention

When it happens

Trigger: mergeAccumulators combining accumulators built from elements with different initial sequences — caused by incorrect keying (elements of different keys funneled into one combine session), or a custom merge implementation bypassing key separation.

Common situations: Using a hot key / fanout that redistributes elements without preserving key identity; feeding the combiner events from multiple sources with unrelated sequence numbering; unit tests merging independent accumulators intentionally.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/1ed2b00492593e41. Report an issue: GitHub.

Appendix: source

Thrown at sdks/java/extensions/ordered/src/main/java/org/apache/beam/sdk/extensions/ordered/combiner/SequenceRangeAccumulator.java:153

      throw new IllegalStateException("First entry is null when initial sequence is set.");
    }
    Long start = firstEntry.getKey();
    Long end = firstEntry.getValue().getLeft();
    Instant latestTimestamp = firstEntry.getValue().getRight();
    // Upper bound is inclusive, but the ContiguousSequenceRange's end is exclusive.
    // The numeric overflow is prevented by dropping the value of Long.MAX.
    return ContiguousSequenceRange.of(start, end + 1, latestTimestamp);
  }

  public int numberOfRanges() {
    return data.size();
  }

  public void merge(SequenceRangeAccumulator another) {
    if (this.initialSequence != null
        && another.initialSequence != null
        && !this.initialSequence.equals(another.initialSequence)) {
      throw new IllegalStateException(
          "Two accumulators contain different initial sequences: "
              + this.initialSequence
              + " and "
              + another.initialSequence);
    }

    if (another.initialSequence != null) {
      long newInitialSequence = another.initialSequence;
      this.initialSequence = newInitialSequence;
      Entry<Long, Pair<Long, Instant>> firstEntry = another.data.firstEntry();
      if (firstEntry != null) {
        Instant timestampOfTheInitialRange = firstEntry.getValue().getRight();
        clearRangesBelowInitialSequence(newInitialSequence, timestampOfTheInitialRange);
      }
    }

    another
        .data

View on GitHub (pinned to 12126d8942)