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
- Read the detailed message: confirm the table exists and is enabled (via HBase shell 'is_enabled').
- Check the connection configuration (quorum, znode, port) matches your cluster and is reachable from workers.
- Retry failed rows — transient region moves/timeouts are common; Beam's sink may or may not retry depending on configuration.
- 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
- Validate the table exists and is enabled before the pipeline starts.
- Confirm HBase quorum/zookeeper config is reachable from every worker node.
- Keep rows/mutations under size limits to avoid DoNotRetryIOException.
- Use unique row keys per element where possible to avoid lock contention.
- Schedule pipelines away from cluster maintenance/split windows.
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
- Connection failures: ECONNREFUSED, ECONNRESET, and friends — why connections get refused, reset, or dropped.
Related errors
- Inputted Schema caused mutation error, check error logs and…
- Mutation type cannot be null.
- Unexpected mutation
- Unexpected mutation type
- Unexpected mutation type
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)