apache/iceberg · critical · UncheckedIOException
Failed to refresh as ref
Error message
Failed to refresh as ref '%s' is no longer valid.
What it means
NessieTableOperations.doRefresh wraps NessieNotFoundException (raised when the ref/hash the client is pinned to no longer exists) into an UncheckedIOException with this message. It means the Nessie reference used by the table's operations was deleted or expired between operations, so the table metadata cannot be refreshed.
Solutions
- Verify the ref still exists (Nessie API: GET /trees/{ref}) and recreate it if deleted.
- Reload the table/catalog against an existing ref so operations are re-pinned.
- Check catalog 'ref'/'asset-ref' config for typos or stale values.
- Ensure branch-deletion automation doesn't remove refs still in use by running jobs.
Example fix
// before
NessieCatalog catalog = ... configured with ref "etl-feature"; // branch deleted
catalog.loadTable(TableIdentifier.of("ns", "t")); // UncheckedIOException
// after
NessieCatalog catalog = ...;
if (catalog.getRef().getReference().getName().equals("etl-feature") && !nessieRefExists("etl-feature")) {
catalog.setRef("main");
}
Table t = catalog.loadTable(TableIdentifier.of("ns", "t")); Defensive patterns
Strategy: retry
Validate before calling
Reference r = nessieApi.ref().refName(refName).get(); // throws NessieNotFoundException if gone
Try / catch
try { table.refresh(); } catch (UncheckedIOException e) { if (e.getCause() instanceof NessieNotFoundException) { catalog.setRef("main"); table = catalog.loadTable(id); } else { throw e; } } Prevention
- Verify branch existence before long jobs and after failures
- Exclude in-use branches from CI cleanup and Nessie GC/retention policies
- Reload table operations instead of retrying on the same dead ref
When it happens
Trigger: Calling any table operation (load, refresh, read, commit) after the Nessie branch/tag the catalog was opened against was deleted, e.g. via branch cleanup or Nessie GC; also stale refs after namespace branch pruning.
Common situations: CI pipelines deleting ephemeral branches while a long-running Spark job still holds the ref; wrong ref name in catalog config after a rename; Nessie retention pruning live refs.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- Failed to refresh as ref
- Cannot update namespace
- No such table
- Cannot create namespace
- Cannot create namespace
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/213588fab69367e5.
Report an issue: GitHub.
Appendix: source
Thrown at nessie/src/main/java/org/apache/iceberg/nessie/NessieTableOperations.java:74
/** Create a nessie table operations given a table identifier. */
NessieTableOperations(ContentKey key, NessieIcebergClient client, FileIO fileIO) {
this.key = key;
this.client = client;
this.fileIO = fileIO;
}
@Override
protected String tableName() {
return key.toString();
}
@Override
protected void doRefresh() {
try {
client.refresh();
} catch (NessieNotFoundException e) {
throw new UncheckedIOException(
String.format(
"Failed to refresh as ref '%s' is no longer valid.", client.getRef().getName()),
e);
}
String metadataLocation = null;
Reference reference = client.getRef().getReference();
try {
Content content = client.getApi().getContent().key(key).reference(reference).get().get(key);
LOG.debug("Content '{}' at '{}': {}", key, reference, content);
if (content == null) {
if (currentMetadataLocation() != null) {
throw new NoSuchTableException("No such table '%s' in '%s'", key, reference);
}
} else {
this.table =
content
.unwrap(IcebergTable.class)
.orElseThrow(() -> new NessieContentNotFoundException(key, reference.getName()));View on GitHub (pinned to 86d9c8fc54)