apache/iceberg · error · UnsupportedOperationException
Using a reference is not supported
Error message
Using a reference is not supported
What it means
The default TableScan.useRef(String) throws UnsupportedOperationException because a TableScan implementation may not support snapshot references (branches/tags). Implementations such as DataTableScan override it; other TableScan implementations (metadata-table scans, custom scans) keep the throwing default.
Solutions
- Obtain the TableScan from table.newScan() on a real data table (DataTableScan supports useRef).
- Instead of useRef, select the snapshot explicitly via useSnapshotId(snapshotId) resolved through table.snapshot(ref) or table.refs().get(ref).
- Catch UnsupportedOperationException and fall back to snapshot-ID-based refinement.
- Override useRef in custom TableScan implementations.
Example fix
// before
TableScan scan = table.newScan().useRef("audit-branch");
// after
Snapshot snap = table.snapshot("audit-branch");
TableScan scan = (snap != null)
? table.newScan().useSnapshotId(snap.snapshotId())
: table.newScan(); Defensive patterns
Strategy: validation
Validate before calling
Snapshot ref = table.snapshot("audit-branch");
if (ref == null) {
throw new IllegalArgumentException("Reference not found: audit-branch");
}
TableScan scan = table.newScan().useSnapshotId(ref.snapshotId()); Type guard
TableScan safeUseRef(Table t, String ref) {
Snapshot s = t.snapshot(ref);
return (s != null) ? t.newScan().useSnapshotId(s.snapshotId()) : t.newScan();
} Try / catch
try {
scan = table.newScan().useRef(ref);
} catch (UnsupportedOperationException e) {
Snapshot s = table.snapshot(ref);
scan = (s != null) ? table.newScan().useSnapshotId(s.snapshotId()) : table.newScan();
} Prevention
- Prefer resolving the reference to a snapshot ID via table.snapshot(ref) — this works on all scans
- Only use useRef on scans from real data tables (DataTableScan)
- Validate the reference exists in table.refs() before refining the scan
When it happens
Trigger: Calling scan.useRef("branch-name") (directly, or via helpers like planTasks/canDeleteUsingMetadata/dataFiles that refine scans with a reference) on a TableScan implementation that doesn't override useRef.
Common situations: Running Spark/Merge-on-read delete planning against metadata tables or custom scans; applying branch/tag-based reads to scan objects obtained from non-standard scan implementations; copy-on-write utilities selecting a snapshot by reference name.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Cannot create or replace branch on non-Iceberg table: $table
- Cannot drop branch on non-Iceberg table: $table
- Altering a view is not supported by catalog:
- Altering a view is not supported by catalog
- Altering a view is not supported by catalog
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/5f0784772ad808a2.
Report an issue: GitHub.
Appendix: source
Thrown at api/src/main/java/org/apache/iceberg/TableScan.java:49
* Create a new {@link TableScan} from this scan's configuration that will use the given snapshot
* by ID.
*
* @param snapshotId a snapshot ID
* @return a new scan based on this with the given snapshot ID
* @throws IllegalArgumentException if the snapshot cannot be found
*/
TableScan useSnapshot(long snapshotId);
/**
* Create a new {@link TableScan} from this scan's configuration that will use the given
* reference.
*
* @param ref reference
* @return a new scan based on the given reference.
* @throws IllegalArgumentException if a reference with the given name could not be found
*/
default TableScan useRef(String ref) {
throw new UnsupportedOperationException("Using a reference is not supported");
}
/**
* Create a new {@link TableScan} from this scan's configuration that will use the most recent
* snapshot as of the given time in milliseconds on the branch in the scan or main if no branch is
* set.
*
* @param timestampMillis a timestamp in milliseconds.
* @return a new scan based on this with the current snapshot at the given time
* @throws IllegalArgumentException if the snapshot cannot be found or time travel is attempted on
* a tag
*/
TableScan asOfTime(long timestampMillis);
/**
* Create a new {@link TableScan} to read appended data from {@code fromSnapshotId} exclusive to
* {@code toSnapshotId} inclusive.
*View on GitHub (pinned to 86d9c8fc54)