apache/iceberg · error · UncheckedIOException

Failed to load iceberg table from table loader

Error message

Failed to load iceberg table from table loader: ${tableLoader}

What it means

IcebergSink's Builder builds the sink by loading the Table via the supplied TableLoader and wrapping it as a SerializableTable. If loadTable() throws IOException — missing/unreadable metadata, unreachable filesystem, or bad credentials — it is rethrown as this UncheckedIOException naming the loader. This happens eagerly at builder time (unlike error 1505, which occurs at operator init).

Solutions

  1. Confirm the TableLoader points to an existing table (verify via the catalog before building the sink).
  2. Ensure Hadoop/cloud configuration and credentials are on the classpath and valid in the submission environment.
  3. Fix the metadata location/warehouse path in the loader configuration.
  4. If the table was recreated, rebuild the sink with a fresh loader rather than reusing a stale one.

Example fix

// before
TableLoader loader = TableLoader.fromCatalog(CatalogLoader.hadoop("hcat", conf, "file:/bad/path"), TableIdentifier.of("db", "t"));
// after
TableLoader loader = TableLoader.fromCatalog(CatalogLoader.hadoop("hcat", conf, warehousePath), TableIdentifier.of("db", "t"));
Defensive patterns

Strategy: try-catch

Validate before calling

try (TableLoader loader = tableLoader) {
  loader.open();
  Table t = loader.loadTable();
  Preconditions.checkArgument(t != null, "Table must load before building IcebergSink");
}

Try / catch

try { buildIcebergSink(); } catch (UncheckedIOException e) {
  if (e.getMessage() != null && e.getMessage().startsWith("Failed to load iceberg table")) {
    LOG.error("TableLoader could not load the table; check catalog config and metadata location", e);
  }
}

Prevention

When it happens

Trigger: Calling IcebergSink.builder().tableLoader(loader)...append() where loader.open()/loadTable() raises IOException: wrong metadata location, table dropped/renamed, TaskManager/client lacking Hadoop or cloud credentials, network partition to HDFS/S3.

Common situations: Pointing the loader at a path that no longer exists after table recreation; submitting jobs from an environment without core-site/hive-site conf; expired S3 tokens; catalog URI misconfiguration in Flink SQL or DataStream API code.

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

Appendix: source

Thrown at flink/v2.1/flink/src/main/java/org/apache/iceberg/flink/sink/IcebergSink.java:919

  }

  private static String defaultSuffix(String uidSuffix, String defaultSuffix) {
    if (uidSuffix == null || uidSuffix.isEmpty()) {
      return defaultSuffix;
    }
    return uidSuffix;
  }

  private static SerializableTable checkAndGetTable(TableLoader tableLoader, Table table) {
    if (table == null) {
      if (!tableLoader.isOpen()) {
        tableLoader.open();
      }

      try (TableLoader loader = tableLoader) {
        return (SerializableTable) SerializableTable.copyOf(loader.loadTable());
      } catch (IOException e) {
        throw new UncheckedIOException(
            "Failed to load iceberg table from table loader: " + tableLoader, e);
      }
    }

    return (SerializableTable) SerializableTable.copyOf(table);
  }

  /**
   * Clean up after removing {@link Builder#tableSchema}
   *
   * @deprecated since 1.10.0, will be removed in 2.0.0. Use {@link #toFlinkRowType(Schema,
   *     ResolvedSchema)} instead.
   */
  @Deprecated
  private static RowType toFlinkRowType(Schema schema, TableSchema requestedSchema) {
    if (requestedSchema != null) {
      // Convert the flink schema to iceberg schema firstly, then reassign ids to match the existing
      // iceberg schema.

View on GitHub (pinned to 86d9c8fc54)