apache/iceberg · error · RuntimeIOException

Failed to close entries while caching changes

Error message

Failed to close entries while caching changes

What it means

A sibling of the reader-close failure on the data path: while caching data-file changes, closing the ManifestReader after iteration throws an IOException, which Iceberg converts to RuntimeIOException with message "Failed to close entries while caching changes".

Source

Thrown at core/src/main/java/org/apache/iceberg/BaseSnapshot.java:324

        Iterables.filter(
            dataManifests(fileIO), manifest -> Objects.equal(manifest.snapshotId(), snapshotId));
    try (CloseableIterable<ManifestEntry<DataFile>> entries =
        new ManifestGroup(fileIO, changedManifests).ignoreExisting().entries()) {
      for (ManifestEntry<DataFile> entry : entries) {
        switch (entry.status()) {
          case ADDED:
            adds.add(entry.file().copy());
            break;
          case DELETED:
            deletes.add(entry.file().copyWithoutStats());
            break;
          default:
            throw new IllegalStateException(
                "Unexpected entry status, not added or deleted: " + entry);
        }
      }
    } catch (IOException e) {
      throw new RuntimeIOException(e, "Failed to close entries while caching changes");
    }

    this.addedDataFiles = adds.build();
    this.removedDataFiles = deletes.build();
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }

    if (o instanceof BaseSnapshot) {
      BaseSnapshot other = (BaseSnapshot) o;
      return this.snapshotId == other.snapshotId()
          && Objects.equal(this.parentId, other.parentId())
          && this.sequenceNumber == other.sequenceNumber()
          && this.timestampMillis == other.timestampMillis()

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Retry the call after table.refresh(); typically transient
  2. Ensure credentials/Tokens are valid for the duration of the scan (refresh STS tokens, session tokens)
  3. Prevent expireSnapshots/orphan cleanup from racing with incremental change readers
  4. Check storage backend health and network stability

Example fix

// before
List<DataFile> added = snapshot.addedDataFiles();
// after
try {
  List<DataFile> added = snapshot.addedDataFiles();
} catch (RuntimeIOException e) {
  table.refresh();
  List<DataFile> added = snapshot.addedDataFiles();
}
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure credentials valid for scan duration
if (credentialsExpiryEpochMs < System.currentTimeMillis() + scanDurationMs) refreshCredentials();

Try / catch

try { snapshot.addedDataFiles(); } catch (RuntimeIOException e) { table.refresh(); retryWithBackoff(); }

Prevention

When it happens

Trigger: Calling addedDataFiles()/removedDataFiles() when the underlying object store fails while closing the manifest reader: connection reset, credentials expiring mid-scan, or the manifest being deleted concurrently.

Common situations: Long-running incremental-scan jobs whose cloud credentials expire; S3/GCS throttling; cleanup jobs removing manifests mid-read.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/010d9fd711ac5b34. Report an issue: GitHub.