apache/iceberg · error · UncheckedIOException

Failed to create tableMaintenance

Error message

Failed to create tableMaintenance 

What it means

IcebergSink.addPostCommitTopology builds the table maintenance (expire snapshots/rewrite data files) operator; an IOException during its construction is wrapped as UncheckedIOException with this message. It means the post-commit maintenance topology could not be initialized, typically because the table cannot be read from the loader.

Source

Thrown at flink/v2.2/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. Inspect the wrapped cause for the underlying IO failure (path missing, auth, timeout).
  2. Verify the table exists and TableLoader config is valid from the cluster.
  3. Ensure filesystem/catalog credentials are available to the JobManager; retry submission after the storage issue resolves.

Example fix

// before: maintenance on missing table
IcebergSink.forRowData(input).table(table).tableLoader(loader).append();
// after: validate first
if (!catalog.tableExists(tableId)) throw new IllegalStateException("table missing");
IcebergSink.forRowData(input).table(catalog.loadTable(tableId)).tableLoader(loader).append();
Defensive patterns

Strategy: validation

Validate before calling

// validate table access and maintenance prerequisites before enabling maintenance
try (TableLoader loader = tableLoader) {
  loader.open();
  Table t = loader.loadTable();
  Preconditions.checkArgument(catalog.tableExists(tableId), "table missing: %s", tableId);
}

Try / catch

try {
  sink.append();
} catch (UncheckedIOException e) {
  if (e.getMessage().startsWith("Failed to create tableMaintenance")) {
    logger.error("maintenance init failed", e.getCause());
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling .append() on an IcebergSink builder with maintenance enabled when the maintenance builder's table access throws IOException — table metadata unreadable, catalog/warehouse unreachable, or credentials missing on the JobManager.

Common situations: HDFS/S3 outage at job startup; kerberos/HMS auth not configured; table deleted between submit and run; misconfigured maintenance properties pointing at missing resources.

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