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

  1. Verify the TableLoader/catalog configuration can load the table before enabling maintenance (test loader.loadTable() locally).
  2. Check cluster-side access to the table's metadata location and required credentials.
  3. Disable table maintenance if unneeded so the post-commit topology is skipped.
  4. 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

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


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/5cb75fe4a1c6d462. Report an issue: GitHub.