apache/iceberg · error · UncheckedIOException

Failed to create tableMaintenance

Error message

Failed to create tableMaintenance 

What it means

IcebergSink.addPostCommitTopology builds the TableMaintenance operator from the maintenance config; if constructing the lock config or the maintenance builder throws an IOException it is wrapped in UncheckedIOException 'Failed to create tableMaintenance'. The cause reveals whether lock-factory setup or IO during operator creation failed.

Source

Thrown at flink/v1.20/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. Read the chained IOException cause for the real failure (lock store connection, missing resource, etc.)
  2. Verify maintenance lock config properties (lock type, lock store URI/credentials) — try running without lock config (no lockType) to isolate
  3. Confirm the lock backend (e.g. JDBC database, ZooKeeper quorum) is reachable from the job manager
  4. Validate each configured maintenance task (expire snapshots, orphan cleanup, rewrite data files) can run against the table with the job's FileIO credentials

Example fix

// before
TableMaintenance.forChangeStream(changeStream, loader)
    .add(RewriteDataFilesExecutor.fromConfig(table)) // throws IOException
    .append();
// after
// ensure lock/task IO deps are configured & reachable first
LockConfig lockConfig = flinkMaintenanceConfig.createLockConfig();
LOG.info("maintenance lock type={}", lockConfig.lockType());
// fix lock store endpoint/credentials, then retry append()
Defensive patterns

Strategy: validation

Validate before calling

// before enabling maintenance, verify lock config resolves and backend is reachable
LockConfig lockConfig = flinkMaintenanceConfig.createLockConfig();
if (StringUtils.isNotEmpty(lockConfig.lockType())) {
  LockFactory f = LockFactoryBuilder.build(lockConfig, tableName); // throws early if misconfigured
  LOG.info("maintenance lock: {} via {}", lockConfig.lockType(), f);
}

Try / catch

try {
  sink.addPostCommitTopology(committables);
} catch (UncheckedIOException e) {
  LOG.error("tableMaintenance creation failed: {}", e.getCause(), e);
  // disable maintenance tasks and resubmit to isolate the cause
  throw e;
}

Prevention

When it happens

Trigger: Enabling table maintenance (maintenance config) where TableMaintenance.Builder.append() or LockFactoryBuilder.build/createLockConfig performs IO that fails — e.g. creating/reaching a distributed lock (JDBC, ZooKeeper) or reading lock-related configuration resources.

Common situations: Misconfigured table.maintenance.* lock properties (bad lock type/URL), lock store database/table unreachable, missing credentials for the lock backend, or a maintenance task builder doing IO that fails (catalog access during task setup).

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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