apache/flink · error · IllegalStateException

other memory segment has been freed.

Error message

other memory segment has been freed.

What it means

In MemorySegment.swapBytes(...), after bounds checking, liveness of both segments is checked in order. If the receiver is alive but the argument seg2 was freed (seg2.address > seg2.addressLimit), it throws IllegalStateException('other memory segment has been freed.') — the error names the second ('other') segment.

Source

Thrown at flink-core/src/main/java/org/apache/flink/core/memory/MemorySegment.java:1590

            final long thisPos = this.address + offset1;
            final long otherPos = seg2.address + offset2;

            if (thisPos <= this.addressLimit - len && otherPos <= seg2.addressLimit - len) {
                // this -> temp buffer
                UNSAFE.copyMemory(
                        this.heapMemory, thisPos, tempBuffer, BYTE_ARRAY_BASE_OFFSET, len);

                // other -> this
                UNSAFE.copyMemory(seg2.heapMemory, otherPos, this.heapMemory, thisPos, len);

                // temp buffer -> other
                UNSAFE.copyMemory(
                        tempBuffer, BYTE_ARRAY_BASE_OFFSET, seg2.heapMemory, otherPos, len);
                return;
            } else if (this.address > this.addressLimit) {
                throw new IllegalStateException("this memory segment has been freed.");
            } else if (seg2.address > seg2.addressLimit) {
                throw new IllegalStateException("other memory segment has been freed.");
            }
        }

        // index is in fact invalid
        throw new IndexOutOfBoundsException(
                String.format(
                        "offset1=%d, offset2=%d, len=%d, bufferSize=%d, address1=%d, address2=%d",
                        offset1, offset2, len, tempBuffer.length, this.address, seg2.address));
    }

    /**
     * Equals two memory segment regions.
     *
     * @param seg2 Segment to equal this segment with
     * @param offset1 Offset of this segment to start equaling
     * @param offset2 Offset of seg2 to start equaling
     * @param length Length of the equaled memory region
     * @return true if equal, false otherwise

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Check seg2.isFreed() (both segments, really) before swapBytes.
  2. Fix ownership so segments in a sort/index structure are released only after the whole structure is torn down.
  3. Remove the freed segment from the swap index when it is released, instead of leaving a stale reference.

Example fix

// before
a.swapBytes(b, tempBuf, off1, off2, len); // 'other ... has been freed.' => b is freed

// after
if (b.isFreed()) {
    throw new IllegalStateException("argument segment already released");
}
a.swapBytes(b, tempBuf, off1, off2, len);
Defensive patterns

Strategy: validation

Validate before calling

if (seg2.isFreed()) {
    throw new IllegalStateException("argument segment already freed");
}
seg1.swapBytes(seg2, tempBuf, off1, off2, len);

Prevention

When it happens

Trigger: Calling swapBytes(a, b, ...) where b was freed earlier — e.g. sorting across segment lists where one segment was released by a spill/cancel path but remains referenced by the index being swapped.

Common situations: Sort buffers spanning multiple segments after partial release; merging spilled runs that reuse segments freed between runs; concurrent cancellation freeing segments still referenced by comparison/swap indices.

Related errors


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