alibaba/canal · error · RuntimeException

Failed rows:{rows}

Error message

Failed rows:{rows}

What it means

Thrown after the TablestoreAdapter flushes all writers and collects Future results, when one or more row writes failed (WriterResult.getFailedRows() is non-empty). The error message includes a delimited list of failure descriptions built from each RowChangeStatus. This indicates that the Table Store (OTS) write API rejected specific rows, not that the flush mechanism itself failed.

Source

Thrown at client-adapter/tablestore/src/main/java/com/alibaba/otter/canal/client/adapter/tablestore/TablestoreAdapter.java:155

            for (Future<WriterResult> future : futureList) {
                try {
                    WriterResult result = future.get();
                    List<WriterResult.RowChangeStatus> failedRows = result.getFailedRows();
                    if (!CollectionUtils.isEmpty(failedRows)) {
                        totalFailedRows.addAll(failedRows);
                    }
                } catch (InterruptedException e) {
                    logger.info("InterruptedException", e);
                    Thread.currentThread().interrupt();
                } catch (ExecutionException e) {
                    throw new RuntimeException(e);
                }
            }

            if (!CollectionUtils.isEmpty(totalFailedRows)) {
                // 认为有失败的请求
                List<String> msgs = totalFailedRows.stream().map(e -> buildErrorMsgForFailedRowChange(e)).collect(Collectors.toList());
                throw new RuntimeException("Failed rows:" + org.springframework.util.StringUtils.collectionToDelimitedString(msgs, ",", "[", "]"));
            }

        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * 组装失败记录的信息
     * @param rowChangeStatus
     * @return
     */
    public static String buildErrorMsgForFailedRowChange(WriterResult.RowChangeStatus rowChangeStatus) {
        StringBuilder sb = new StringBuilder("{Exception:");
        sb.append(rowChangeStatus.getException().getMessage()).append(",Table:")
        .append(rowChangeStatus.getRowChange().getTableName()).append(",PrimaryKey:")
        .append("{").append(rowChangeStatus.getRowChange().getPrimaryKey().toString())
        .append("}}");

View on GitHub (pinned to 87be50e876)

Solutions

  1. Inspect the error message details for each failed row — they typically contain the OTS error code (e.g. OTSQuotaExhausted, OTSParameterInvalid, OTSRowOperationConflict).
  2. If throttled, increase the OTS table's provisioned read/write capacity or enable auto-scaling.
  3. If PK conflicts, verify source data uniqueness and the PK mapping configuration.
  4. If data type mismatch, align the OTS table column types with the tablestore fieldType mapping in the config.
  5. For transient errors, implement retry logic or a dead-letter queue for failed rows.

Example fix

// increase OTS table throughput if throttled
// via Alibaba Cloud console or CLI:
// ots update-table --instance <name> --read-capacity 5000 --write-capacity 5000

// also check mapping config fieldType alignment
dbMapping:
  targetColumns:
    pk_col:
      type: INTEGER
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

null

Try / catch

// Retry with backoff for transient OTS write failures
int maxRetries = 3;
for (int attempt = 1; attempt <= maxRetries; attempt++) {
    try {
        tablestoreAdapter.sync(dmls);
        break;
    } catch (RuntimeException e) {
        if (e.getMessage() != null && e.getMessage().contains("Failed rows:") && attempt < maxRetries) {
            logger.warn("OTS write failed on attempt {}/{}, retrying...", attempt, maxRetries);
            Thread.sleep(attempt * 2000L);
            continue;
        }
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling the sync/flush path where one or more async writes to Alibaba Tablestore (OTS) returned errors — common causes include primary key conflicts (duplicate PK), throughput exceeded (throttling), data type mismatches, row size limits exceeded, or transient network issues between canal and the OTS endpoint.

Common situations: OTS table provisioned throughput is too low for the binlog event rate; duplicate PK values from source data; column data types in OTS don't match the mapping; network latency or intermittent connectivity to the OTS endpoint; large rows exceeding OTS row size limits.

Related errors


AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14). Data as JSON: /api/errors/4db5d36a8a80d833. Report an issue: GitHub.