apache/iceberg · error · UncheckedIOException

Failed to create tableMaintenance

Error message

Failed to create tableMaintenance 

What it means

IcebergSink.addPostCommitTopology wraps IOException from building the table maintenance (compaction/expire/orphan-cleanup) topology and rethrows it as UncheckedIOException. It means the sink could not append the maintenance operators to the job graph, typically because the underlying catalog/table operations failed with IO problems.

Source

Thrown at flink/v2.3/flink/src/main/java/org/apache/iceberg/flink/sink/IcebergSink.java:305

      } else {
        builder = TableMaintenance.forChangeStream(tableChangeStream, tableLoader);
      }

      builder
          .uidSuffix(tableMaintenanceUid)
          .add(maintenanceTasks)
          .rateLimit(Duration.ofSeconds(flinkMaintenanceConfig.rateLimit()))
          .lockCheckDelay(Duration.ofSeconds(flinkMaintenanceConfig.lockCheckDelay()))
          .parallelism(flinkMaintenanceConfig.parallelism());

      String slotSharingGroup = flinkMaintenanceConfig.slotSharingGroup();
      if (slotSharingGroup != null) {
        builder.slotSharingGroup(slotSharingGroup);
      }

      builder.append();
    } catch (IOException e) {
      throw new UncheckedIOException("Failed to create tableMaintenance ", e);
    }
  }

  @Override
  public DataStream<RowData> addPreWriteTopology(DataStream<RowData> inputDataStream) {
    return distributeDataStream(inputDataStream);
  }

  @Override
  public DataStream<CommittableMessage<IcebergCommittable>> addPreCommitTopology(
      DataStream<CommittableMessage<WriteResult>> writeResults) {
    TypeInformation<CommittableMessage<IcebergCommittable>> typeInformation =
        CommittableMessageTypeInfo.of(this::getCommittableSerializer);

    String suffix = defaultSuffix(uidSuffix, table.name());
    String preCommitAggregatorUid = String.format("Sink pre-commit aggregator: %s", suffix);

    // global forces all output records send to subtask 0 of the downstream committer operator.

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Verify the catalog and filesystem are reachable and credentials are valid before submitting the Flink job
  2. Confirm the table still exists at the loader's location and the table loader configuration is correct
  3. Inspect the wrapped IOException cause for the concrete storage/catalog failure and fix that root cause
  4. Disable or reconfigure table maintenance (maintenance settings) if it is not needed for this sink
Defensive patterns

Strategy: try-catch

Validate before calling

Table t;
try (TableLoader loader = tableLoader) {
  loader.open();
  t = loader.loadTable(); // fail fast before building the sink
}

Try / catch

try {
  sinkBuilder.append();
} catch (UncheckedIOException e) {
  Throwable root = ExceptionUtils.getRootCause(e);
  LOG.error("table maintenance topology failed: {}", root.getMessage(), root);
  throw e;
}

Prevention

When it happens

Trigger: Calling IcebergSink builder with maintenance enabled (tableLoader-based catalogs) where TableMaintenance's internal catalog/table access throws IOException while appending the topology via builder.append().

Common situations: Catalog storage (HDFS/S3) unavailable or misconfigured when the sink starts; credentials expired so metadata files can't be read; table deleted/moved between sink construction and topology build.

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