apache/iceberg · error · RuntimeIOException

Failed to read file: %s

Error message

Failed to read file: %s

What it means

read() opens a metadata JSON file (auto-detecting gzip by the .gz/.metadata.json.gz filename), parses it with Jackson, and builds TableMetadata. Any IOException reading or decompressing the file becomes RuntimeIOException 'Failed to read file: <location>'. It means the metadata file itself could not be read or decompressed.

Source

Thrown at core/src/main/java/org/apache/iceberg/TableMetadataParser.java:305

    generator.writeObjectFieldStart(REFS);
    for (Map.Entry<String, SnapshotRef> refEntry : refs.entrySet()) {
      generator.writeFieldName(refEntry.getKey());
      SnapshotRefParser.toJson(refEntry.getValue(), generator);
    }
    generator.writeEndObject();
  }

  public static TableMetadata read(FileIO io, String path) {
    return read(io.newInputFile(path));
  }

  public static TableMetadata read(InputFile file) {
    Codec codec = Codec.fromFileName(file.location());
    try (InputStream is =
        codec == Codec.GZIP ? new GZIPInputStream(file.newStream()) : file.newStream()) {
      return fromJson(file, JsonUtil.mapper().readValue(is, JsonNode.class));
    } catch (IOException e) {
      throw new RuntimeIOException(e, "Failed to read file: %s", file.location());
    }
  }

  /**
   * Read TableMetadata from a JSON string.
   *
   * <p>The TableMetadata's metadata file location will be unset.
   *
   * @param json a JSON string of table metadata
   * @return a TableMetadata object
   */
  public static TableMetadata fromJson(String json) {
    return fromJson(null, json);
  }

  /**
   * Read TableMetadata from a JSON string.
   *

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Confirm the metadata file exists and is readable at file.location() via the catalog
  2. If the file is corrupt, roll back the catalog pointer to a valid earlier metadata version
  3. Check that gzip naming matches actual content: only .metadata.json.gz files are gzip-decoded
  4. Inspect the chained cause (e.g. FileNotFoundException, permission error) and fix storage access

Example fix

// before
TableMetadata meta = TableMetadataParser.read(fileIO.newInputFile(oldLoc)); // file moved
// after
String loc = table.operations().current().metadataFileLocation();
TableMetadata meta = TableMetadataParser.read(fileIO.newInputFile(loc));
Defensive patterns

Strategy: try-catch

Validate before calling

InputFile f = fileIO.newInputFile(loc);
if (!f.exists()) { throw new FileNotFoundException("Metadata file missing: " + loc); }

Try / catch

try { return TableMetadataParser.read(file); }
catch (RuntimeIOException e) {
  throw new IllegalStateException("Unreadable metadata " + file.location()
      + "; cause: " + e.getCause(), e);
}

Prevention

When it happens

Trigger: TableMetadataParser.read(InputFile) on a metadata file that is missing or truncated, unreadable (permissions), or corrupt gzip stream (e.g. a .metadata.json.gz file that is not actually gzipped); FileIO.newStream() throwing IOException.

Common situations: Stale/invalid table metadata location after manual catalog edits or bucket recreation; partial upload of metadata files; pointing a static table at a deleted or moved metadata file; decompressing a file saved without gzip despite the .gz name.

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/741d44405271ae25. Report an issue: GitHub.