apache/beam · error · RuntimeException

Table row mutation failed. Table Available/Enabled…

Error message

Table %s row %s mutation failed. 
Table Available/Enabled: %s %s 
Connection Closed/Aborted/Locks: %s %s

What it means

HBaseIO's write uses Table.mutateRow to preserve mutation ordering. If mutateRow throws an IOException, it is wrapped in a RuntimeException containing the table id, row key, and diagnostics about table availability, connection state, and locks so the failing row can be identified.

Solutions

  1. Read the detailed message: confirm the table exists and is enabled (via HBase shell 'is_enabled').
  2. Check the connection configuration (quorum, znode, port) matches your cluster and is reachable from workers.
  3. Retry failed rows — transient region moves/timeouts are common; Beam's sink may or may not retry depending on configuration.
  4. Verify row/mutation size limits (e.g. hbase.client.keyvalue.maxsize) if the cause is DoNotRetryIOException.

Example fix

// before
HBaseIO.write().to("wrong_table").withConfiguration(conf);
// after
try (Connection c = ConnectionFactory.createConnection(conf);
     Admin admin = c.getAdmin()) {
  if (!admin.tableExists(TableName.valueOf("my_table")) || !admin.isTableEnabled(TableName.valueOf("my_table"))) {
    throw new IllegalStateException("table missing or disabled");
  }
}
HBaseIO.write().to("my_table").withConfiguration(conf);
Defensive patterns

Strategy: retry

Validate before calling

try (Connection c = ConnectionFactory.createConnection(conf); Admin admin = c.getAdmin()) {
  TableName tn = TableName.valueOf(tableId);
  if (!admin.tableExists(tn)) throw new IllegalStateException("table missing: " + tableId);
  if (!admin.isTableEnabled(tn)) throw new IllegalStateException("table disabled: " + tableId);
}

Try / catch

try {
  table.mutateRow(mutations);
} catch (IOException e) {
  if (e instanceof RetriesExhaustedException || e instanceof SocketTimeoutException) {
    // transient: backoff and retry once
    sleepBackoff();
    table.mutateRow(mutations);
  } else {
    throw new RuntimeException("permanent mutation failure for row " + Bytes.toString(mutations.getRow()), e);
  }
}

Prevention

When it happens

Trigger: Writing a batch of mutations to HBase via HBaseIO.write().to() when table.mutateRow(mutations) throws IOException — server unavailable, region moved, table disabled, connection closed, row lock conflict, or RPC timeout.

Common situations: HBase cluster unreachable from workers; table disabled or being split; RegionServer restart during write; DoNotRetryIOException from an oversized row/mutation; wrong table name.

Understand the failure class

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/3c69d3d512a4b048. Report an issue: GitHub.

Appendix: source

Thrown at sdks/java/io/hbase/src/main/java/org/apache/beam/sdk/io/hbase/HBaseIO.java:1020

          HBaseSharedConnection.close(configuration);
        } catch (Exception e) {
          failure = appendSuppressed(failure, e);
        }
        if (failure != null) {
          rethrowCloseFailure(failure);
        }
      }

      @ProcessElement
      public void processElement(ProcessContext c) throws IOException {
        RowMutations mutations = c.element().getValue();

        try {
          // Use Table instead of BufferedMutator to preserve mutation-ordering
          table.mutateRow(mutations);
          recordsWritten++;
        } catch (IOException e) {
          throw new RuntimeException(
              String.join(
                  " ",
                  "Table",
                  tableId,
                  "row",
                  Bytes.toString(mutations.getRow()),
                  "mutation failed.",
                  "\nTable Available/Enabled:",
                  Boolean.toString(
                      connection.getAdmin().isTableAvailable(TableName.valueOf(tableId))),
                  Boolean.toString(
                      connection.getAdmin().isTableEnabled(TableName.valueOf(tableId))),
                  "\nConnection Closed/Aborted/Locks:",
                  Boolean.toString(connection.isClosed()),
                  Boolean.toString(connection.isAborted())));
        }
      }

View on GitHub (pinned to 12126d8942)