apache/iceberg · error · IllegalStateException

Cannot find snapshot older than

Error message

Cannot find snapshot older than 

What it means

SnapshotUtil.oldestAncestorAfter walks the snapshot ancestry chain looking for the oldest ancestor whose committed timestamp is after the given timestamp. If it exhausts the chain (no snapshot in the lineage is newer than the timestamp and the chain terminates without a null-parent snapshot in range), it throws this IllegalStateException.

Solutions

  1. Verify the requested timestamp is within the retained snapshot history (check current snapshot's ancestor list first)
  2. Guard with SnapshotUtil.ancestorIds/table.currentSnapshot() and handle the empty-lineage case before calling
  3. Lower retention (increase maxSnapshotAgeMs / minSnapshotsToKeep) so old ancestors are not expired
  4. Catch IllegalStateException and treat as 'no snapshot older than timestamp' for your use case

Example fix

// before
Snapshot s = SnapshotUtil.oldestAncestorAfter(table, tsMillis);
// after
List<Long> ancestors = SnapshotUtil.snapshotIds(table.currentSnapshot(), null); // or ancestorIds
Optional-style guard:
Snapshot s = ancestors.isEmpty() ? null : SnapshotUtil.oldestAncestorAfter(table, Math.max(tsMillis, firstCommitTs));
Defensive patterns

Strategy: validation

Validate before calling

long oldestTs = SnapshotUtil.oldestAncestorTs != null ? ... : table.currentSnapshot().timestampMillis();
if (tsMillis < SnapshotUtil.ancestorIds(table.currentSnapshot(), null).size() && firstCommitTs > tsMillis) {
  // timestamp predates retained history — handle before calling
}

Prevention

When it happens

Trigger: Calling oldestAncestorAfter(table, timestampMillis) with a timestamp older than the table's first snapshot commit, or on a lineage where the ancestor walk cannot find a snapshot older than the timestamp (e.g. expired snapshots removed the chain tail).

Common situations: Incremental/incremental-consumer logic computing changes since a timestamp that predates snapshot expiry (expireSnapshots removed old ancestors); passing millis vs seconds confusion; computing on an empty/new table with a future timestamp.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/16a71a5ac22ac6d1. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/org/apache/iceberg/util/SnapshotUtil.java:182

    }

    Snapshot lastSnapshot = null;
    for (Snapshot snapshot : currentAncestors(table)) {
      if (snapshot.timestampMillis() < timestampMillis) {
        return lastSnapshot;
      } else if (snapshot.timestampMillis() == timestampMillis) {
        return snapshot;
      }

      lastSnapshot = snapshot;
    }

    if (lastSnapshot != null && lastSnapshot.parentId() == null) {
      // this is the first snapshot in the table, return it
      return lastSnapshot;
    }

    throw new IllegalStateException(
        "Cannot find snapshot older than " + DateTimeUtil.formatTimestampMillis(timestampMillis));
  }

  /**
   * Returns list of snapshot ids in the range - (fromSnapshotId, toSnapshotId]
   *
   * <p>This method assumes that fromSnapshotId is an ancestor of toSnapshotId.
   */
  public static List<Long> snapshotIdsBetween(Table table, long fromSnapshotId, long toSnapshotId) {
    List<Long> snapshotIds =
        Lists.newArrayList(
            ancestorIds(
                table.snapshot(toSnapshotId),
                snapshotId -> snapshotId != fromSnapshotId ? table.snapshot(snapshotId) : null));
    return snapshotIds;
  }

  public static Iterable<Long> ancestorIdsBetween(

View on GitHub (pinned to 86d9c8fc54)