apache/iceberg · error · UnsupportedOperationException
%s doesn't implement cleanupLevel
Error message
%s doesn't implement cleanupLevel
What it means
ExpireSnapshots.cleanupLevel is a default interface method that throws unless the concrete implementation supports choosing a cleanup level. The default exists so implementations predating CleanupLevel remain source-compatible; calling it on such an implementation surfaces this error naming the implementing class.
Source
Thrown at api/src/main/java/org/apache/iceberg/ExpireSnapshots.java:150
ExpireSnapshots cleanExpiredFiles(boolean clean);
/**
* Configures the cleanup level for expired files.
*
* <p>This method provides fine-grained control over which files are cleaned up during snapshot
* expiration.
*
* <p>Consider {@link CleanupLevel#METADATA_ONLY} when data files are shared across tables or when
* using procedures like add-files that may reference the same data files.
*
* <p>Consider {@link CleanupLevel#NONE} when data and metadata files may be more efficiently
* removed using a distributed framework through the actions API.
*
* @param level the cleanup level to use for expired snapshots
* @return this for method chaining
*/
default ExpireSnapshots cleanupLevel(CleanupLevel level) {
throw new UnsupportedOperationException(
this.getClass().getName() + " doesn't implement cleanupLevel");
}
/**
* Enable cleaning up unused metadata, such as partition specs, schemas, etc.
*
* @param clean remove unused partition specs, schemas, or other metadata when true
* @return this for method chaining
*/
default ExpireSnapshots cleanExpiredMetadata(boolean clean) {
throw new UnsupportedOperationException(
this.getClass().getName() + " doesn't implement cleanExpiredMetadata");
}
}
View on GitHub (pinned to 86d9c8fc54)
Solutions
- Upgrade the implementation (Spark/Flink actions or custom class) to one that overrides cleanupLevel
- Remove the cleanupLevel(...) call and rely on the implementation's default cleanup behavior
- If you own the implementation, implement cleanupLevel to store and honor the CleanupLevel
Example fix
// before
expireSnapshots.cleanupLevel(CleanupLevel.LOCAL).execute();
// after
if (supportsCleanupLevel) {
expireSnapshots.cleanupLevel(CleanupLevel.LOCAL).execute();
} else {
expireSnapshots.execute();
} Defensive patterns
Strategy: try-catch
Validate before calling
// no pre-call capability flag exists; feature-gate by implementation type
boolean supportsCleanupLevel = !(expireSnapshots.getClass().getName().startsWith("legacy.")); Try / catch
try {
expireSnapshots.cleanupLevel(CleanupLevel.LOCAL);
} catch (UnsupportedOperationException e) {
LOG.warn("cleanupLevel unsupported: {}", e.getMessage());
} Prevention
- Upgrade implementations before using newer ExpireSnapshots options
- Feature-detect: attempt the call in a guarded path on startup
- Keep custom ExpireSnapshots overrides in sync with the interface
When it happens
Trigger: Calling expireSnapshots.cleanupLevel(CleanupLevel.X) on an ExpireSnapshots implementation (e.g. an older catalog or third-party action) that has not overridden cleanupLevel.
Common situations: Mixing a newer Iceberg API client with an older engine/catalog implementation; custom ExpireSnapshots implementations that were not updated when CleanupLevel was introduced.
Related errors
- %s doesn't implement cleanExpiredMetadata
- %s doesn't implement copyWithStats
- %s doesn't implement validateFilesExist
- Can't retrieve values from an empty struct
- Can't modify an empty struct
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/74f19f9be50ab6e0.
Report an issue: GitHub.