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
- Confirm the metadata file exists and is readable at file.location() via the catalog
- If the file is corrupt, roll back the catalog pointer to a valid earlier metadata version
- Check that gzip naming matches actual content: only .metadata.json.gz files are gzip-decoded
- 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
- Resolve the metadata path from the catalog pointer, not cached/stale strings
- Verify .gz naming matches actual gzip content before reading
- Keep multiple metadata versions and roll back the pointer if one is corrupt
- Check bucket/object existence after storage lifecycle or migration operations
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
- RuntimeIOException
- Failed to close manifest reader
- Failed to close entries while caching changes
- Failed to read manifest file: %s
- Failed to close manifest list: %s
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/741d44405271ae25.
Report an issue: GitHub.