apache/iceberg · error · IllegalArgumentException
Cannot find a snapshot after: {scanContext.startSnapshotTime
Error message
Cannot find a snapshot after: {scanContext.startSnapshotTimestamp()} What it means
ContinuousSplitPlannerImpl.startSnapshot uses SnapshotUtil.oldestAncestorAfter to find the first snapshot at or after startSnapshotTimestamp for the TABLE_SCAN_THEN_INCREMENT/INCREMENTAL_FROM_TIMESTAMP strategy; Preconditions.checkArgument fails when no snapshot matches, meaning no snapshot exists after the given timestamp.
Source
Thrown at flink/v2.1/flink/src/main/java/org/apache/iceberg/flink/source/enumerator/ContinuousSplitPlannerImpl.java:244
case INCREMENTAL_FROM_LATEST_SNAPSHOT_EXCLUSIVE:
return Optional.ofNullable(table.currentSnapshot());
case INCREMENTAL_FROM_EARLIEST_SNAPSHOT:
return Optional.ofNullable(SnapshotUtil.oldestAncestor(table));
case INCREMENTAL_FROM_SNAPSHOT_ID:
Snapshot matchedSnapshotById = table.snapshot(scanContext.startSnapshotId());
Preconditions.checkArgument(
matchedSnapshotById != null,
"Start snapshot id not found in history: " + scanContext.startSnapshotId());
return Optional.of(matchedSnapshotById);
case INCREMENTAL_FROM_SNAPSHOT_TIMESTAMP:
Snapshot matchedSnapshotByTimestamp =
SnapshotUtil.oldestAncestorAfter(table, scanContext.startSnapshotTimestamp());
Preconditions.checkArgument(
matchedSnapshotByTimestamp != null,
"Cannot find a snapshot after: " + scanContext.startSnapshotTimestamp());
return Optional.of(matchedSnapshotByTimestamp);
default:
throw new IllegalArgumentException(
"Unknown starting strategy: " + scanContext.streamingStartingStrategy());
}
}
}
View on GitHub (pinned to 86d9c8fc54)
Solutions
- Check table.currentSnapshot().timestampMillis() and pick a start timestamp earlier than the oldest retained snapshot.
- Remove the start-snapshot-timestamp option to start from the current snapshot instead.
- Verify the timestamp is in milliseconds since epoch and not skewed by timezone or unit conversion.
- Reduce snapshot expiration aggressiveness so retained history covers the requested timestamp.
Example fix
// before
options.put("start-snapshot-timestamp", String.valueOf(System.currentTimeMillis() + drift));
// after
Snapshot s = table.currentSnapshot();
if (s != null && s.timestampMillis() > ts) {
ts = s.timestampMillis(); // clamp to a known snapshot time
}
options.put("start-snapshot-timestamp", String.valueOf(ts)); Defensive patterns
Strategy: validation
Validate before calling
long ts = ...; // desired start timestamp in millis
Table table = tableLoader.loadTable();
long oldest = SnapshotUtil.oldestAncestor(table) != null
? SnapshotUtil.oldestAncestor(table).timestampMillis() : Long.MAX_VALUE;
if (ts < oldest || ts > Optional.ofNullable(table.currentSnapshot())
.map(Snapshot::timestampMillis).orElse(Long.MAX_VALUE)) {
ts = oldest; // clamp to a retained snapshot
} Prevention
- Clamp start timestamps to the range of retained snapshots before configuring the source.
- Use StartupOptions.snapshot() with a known snapshot ID instead of a raw timestamp.
- Reconcile timestamp units (always epoch millis) and timezone sources.
- Coordinate snapshot expiration with streaming readers.
When it happens
Trigger: Starting a streaming/incremental scan with start-snapshot-timestamp set to a time after the table's latest snapshot (or before which all snapshots were expired), so no ancestor snapshot matches.
Common situations: Timestamp computed from another table or an older run; table is brand new or all snapshots have been expired by retention rules; timezone/unit mistakes (seconds vs milliseconds) pushing the timestamp past the last snapshot.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Unsupported timestamp type: ${timestamps}
- Unknown type for long field. Type name: ${className}
- Unknown type for timestamp_ns: ${class}
- Unsupported type: ${type}
- Unexpected object type for TIMESTAMP logical type. Received:
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/055f1928b2791b50.
Report an issue: GitHub.