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

  1. Verify the ref still exists (Nessie API: GET /trees/{ref}) and recreate it if deleted.
  2. Reload the table/catalog against an existing ref so operations are re-pinned.
  3. Check catalog 'ref'/'asset-ref' config for typos or stale values.
  4. 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

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


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)