apache/iceberg · error · ValidationException

Metadata file for version

Error message

Metadata file for version %d is missing under %s

What it means

HadoopTableOperations.refresh() resolves the current table version by looking for version-hint/metadata files under the metadata root. If no metadata file exists for the expected version (and it is not the legitimate v0 case), the library cannot determine table state and throws ValidationException. This guards against a corrupt or truncated metadata directory.

Solutions

  1. Verify version-hint.text matches an existing v<N>.metadata.json.gz/json file in the metadata directory and correct the hint file if stale
  2. Restore the missing metadata file from backup or object-store versioning
  3. List the metadata directory for the highest valid version file and rewrite version-hint.text to that version
  4. Rebuild the table by re-importing data if metadata is unrecoverable

Example fix

// before: hint file says version 5 but only v4 exists
// after: fix hint file to the highest existing version
echo 4 > /warehouse/db/table/metadata/version-hint.text
Defensive patterns

Strategy: validation

Validate before calling

Path meta = new Path(root, "metadata");
if (!fs.exists(new Path(meta, "version-hint.text")) ||
    !fs.globStatus(new Path(meta, "v*.metadata.json*"), p -> true).isEmpty()) {
  // proceed only when hint and metadata files agree
}

Try / catch

try { table.refresh(); } catch (ValidationException e) { /* inspect metadata dir, repair version-hint.text */ }

Prevention

When it happens

Trigger: Calling Table.refresh() / current() when version-hint.text points at version N but vN.metadata.json is absent under the metadata root; concurrent writers or manual deletion of metadata files; the temporary-operations path resolving a version that was never committed.

Common situations: Manually deleting or moving metadata JSON files; a failed/incomplete commit left version-hint.text ahead of actual files; writing the table to HDFS and then copying only data files; race between two writers where one renames files away.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at core/src/main/java/org/apache/iceberg/hadoop/HadoopTableOperations.java:110

  private synchronized void updateVersionAndMetadata(int newVersion, String metadataFile) {
    // update if the current version is out of date
    if (version == null || version != newVersion) {
      this.version = newVersion;
      this.currentMetadata =
          checkUUID(currentMetadata, TableMetadataParser.read(io(), metadataFile));
    }
  }

  @Override
  public TableMetadata refresh() {
    int ver = version != null ? version : findVersion();
    try {
      Path metadataFile = getMetadataFile(ver);
      if (version == null && metadataFile == null && ver == 0) {
        // no v0 metadata means the table doesn't exist yet
        return null;
      } else if (metadataFile == null) {
        throw new ValidationException(
            "Metadata file for version %d is missing under %s", ver, metadataRoot());
      }

      Path nextMetadataFile = getMetadataFile(ver + 1);
      while (nextMetadataFile != null) {
        ver += 1;
        metadataFile = nextMetadataFile;
        nextMetadataFile = getMetadataFile(ver + 1);
      }

      updateVersionAndMetadata(ver, metadataFile.toString());

      this.shouldRefresh = false;
      return currentMetadata;
    } catch (IOException e) {
      throw new RuntimeIOException(e, "Failed to refresh the table");
    }
  }

View on GitHub (pinned to 86d9c8fc54)