{"record":{"id":"16a71a5ac22ac6d1","repo":"apache/iceberg","slug":"cannot-find-snapshot-older-than","errorCode":null,"errorMessage":"Cannot find snapshot older than ","messagePattern":"Cannot find snapshot older than ","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/util/SnapshotUtil.java","lineNumber":182,"sourceCode":"    }\n\n    Snapshot lastSnapshot = null;\n    for (Snapshot snapshot : currentAncestors(table)) {\n      if (snapshot.timestampMillis() < timestampMillis) {\n        return lastSnapshot;\n      } else if (snapshot.timestampMillis() == timestampMillis) {\n        return snapshot;\n      }\n\n      lastSnapshot = snapshot;\n    }\n\n    if (lastSnapshot != null && lastSnapshot.parentId() == null) {\n      // this is the first snapshot in the table, return it\n      return lastSnapshot;\n    }\n\n    throw new IllegalStateException(\n        \"Cannot find snapshot older than \" + DateTimeUtil.formatTimestampMillis(timestampMillis));\n  }\n\n  /**\n   * Returns list of snapshot ids in the range - (fromSnapshotId, toSnapshotId]\n   *\n   * <p>This method assumes that fromSnapshotId is an ancestor of toSnapshotId.\n   */\n  public static List<Long> snapshotIdsBetween(Table table, long fromSnapshotId, long toSnapshotId) {\n    List<Long> snapshotIds =\n        Lists.newArrayList(\n            ancestorIds(\n                table.snapshot(toSnapshotId),\n                snapshotId -> snapshotId != fromSnapshotId ? table.snapshot(snapshotId) : null));\n    return snapshotIds;\n  }\n\n  public static Iterable<Long> ancestorIdsBetween(","sourceCodeStart":164,"sourceCodeEnd":200,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/util/SnapshotUtil.java#L164-L200","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Verify the requested timestamp is within the retained snapshot history (check current snapshot's ancestor list first)","Guard with SnapshotUtil.ancestorIds/table.currentSnapshot() and handle the empty-lineage case before calling","Lower retention (increase maxSnapshotAgeMs / minSnapshotsToKeep) so old ancestors are not expired","Catch IllegalStateException and treat as 'no snapshot older than timestamp' for your use case"],"exampleFix":"// before\nSnapshot s = SnapshotUtil.oldestAncestorAfter(table, tsMillis);\n// after\nList<Long> ancestors = SnapshotUtil.snapshotIds(table.currentSnapshot(), null); // or ancestorIds\nOptional-style guard:\nSnapshot s = ancestors.isEmpty() ? null : SnapshotUtil.oldestAncestorAfter(table, Math.max(tsMillis, firstCommitTs));","handlingStrategy":"validation","validationCode":"long oldestTs = SnapshotUtil.oldestAncestorTs != null ? ... : table.currentSnapshot().timestampMillis();\nif (tsMillis < SnapshotUtil.ancestorIds(table.currentSnapshot(), null).size() && firstCommitTs > tsMillis) {\n  // timestamp predates retained history — handle before calling\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Check the timestamp falls within retained snapshot history before calling","Avoid expiring ancestors that incremental consumers still need","Convert timestamps to millis consistently","Catch IllegalStateException as a 'no data that old' signal"],"tags":["snapshots","timestamp"],"backgroundTag":"empty-result-set","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}