apache/iceberg · error · UncheckedIOException
Failed to create tableMaintenance
Error message
Failed to create tableMaintenance
What it means
IcebergSink.addPostCommitTopology() attaches the table maintenance topology, which involves I/O (e.g. opening the table/loader while building maintenance operators). An IOException during that construction is wrapped in this UncheckedIOException, meaning maintenance topology setup failed — the write itself is not what failed.
Source
Thrown at flink/v2.1/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
- Verify the TableLoader/catalog configuration can load the table before enabling maintenance (test loader.loadTable() locally).
- Check cluster-side access to the table's metadata location and required credentials.
- Disable table maintenance if unneeded so the post-commit topology is skipped.
- Fix the underlying IOException reported as the cause of this UncheckedIOException.
Defensive patterns
Strategy: try-catch
Validate before calling
try (TableLoader loader = cfg.tableLoader()) {
loader.open();
loader.loadTable(); // verify maintenance can load the table before enabling it
} Try / catch
try { sink.append(); } catch (UncheckedIOException e) {
if (e.getMessage() != null && e.getMessage().startsWith("Failed to create tableMaintenance")) {
LOG.error("Maintenance topology setup failed; check table access for maintenance", e);
}
} Prevention
- Validate the TableLoader can load the table on the job cluster before enabling maintenance.
- Ensure maintenance-related catalog configs and credentials are present cluster-wide.
- Disable maintenance in environments where the table is read-only or inaccessible.
When it happens
Trigger: Calling IcebergSink.builder()...append() with table maintenance enabled while an IOException occurs building the maintenance sub-topology, e.g. TableLoader cannot open the table or the maintenance catalog resources can't be accessed.
Common situations: Maintenance enabled with a table whose metadata can't be loaded on the job manager; wrong catalog configuration for maintenance; permissions issues reading table metadata in the cluster environment.
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
- Failed to create tableMaintenance
- Failed to load iceberg table from table loader:
- Failed to load iceberg table from table loader: ${tableLoade
- Failed to create tableMaintenance
- Failed to list partitions of table %s
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/5cb75fe4a1c6d462.
Report an issue: GitHub.