apache/seatunnel · warning

Multi-table sink encountered non-row-level error in plugin

Error message

Multi-table sink encountered non-row-level error in plugin [{}], table [{}]: {}

What it means

EngineMultiTableRowErrorHandler.handleRowError classifies sink exceptions as row-level (skippable) or system-level (fatal). When isRowError() returns false, this warning is logged with plugin and table context and the handler returns false, telling the caller to treat the error as fatal. It means a write failure occurred that the error-handling policy does not consider safe to skip per-row.

Solutions

  1. Read the chained cause: fix the underlying system-level issue (connectivity, permissions, schema) and rerun.
  2. If the exception is genuinely row-scoped but misclassified, upgrade/patch the connector's exception classification.
  3. Adjust job design so system-level failures are retried rather than skipped — do not force these to be skippable.
  4. If too many such warnings appear, check whether the error-handling config (row vs system errors) matches the connector's behavior.

Example fix

null
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

null

Try / catch

boolean handled = rowErrorHandler.handleRowError(writer, tableId, row, t);
if (!handled) {
    // system-level failure: rethrow as fatal
    throw new RuntimeException("Non-row-level sink error in " + tableId, t);
}

Prevention

When it happens

Trigger: A multi-table sink write throws an exception not classified as a row error (e.g. connection loss, serialization failure of the whole batch, null writer, generic RuntimeException/SQLException not in the row-error categories), with row error handling enabled.

Common situations: Error-sink/target database outage during a run; batch-level failures where individual rows cannot be identified; misconfigured error handler expecting all failures to be skippable; connector throwing wrapper exceptions not recognized by isRowError.

Related errors


AI-assisted analysis of apache/seatunnel@cf67b549a7 (2026-09-10). Data as JSON: /api/errors/d5cde2ae5cf7495c. Report an issue: GitHub.

Appendix: source

Thrown at seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/task/error/EngineMultiTableRowErrorHandler.java:75

            RowErrorClassifier<SeaTunnelRow> rowErrorClassifier,
            String pluginName,
            BiConsumer<SeaTunnelRow, ErrorHandlingSinkWriter.WriteOutcome> outcomeConsumer,
            EngineRowErrorCollector rowErrorCollector) {
        this.errorHandler = errorHandler;
        this.rowErrorClassifier = rowErrorClassifier;
        this.pluginName = pluginName;
        this.outcomeConsumer = outcomeConsumer;
        this.rowErrorCollector = rowErrorCollector;
    }

    @Override
    public boolean handleRowError(
            SinkWriter<SeaTunnelRow, ?, ?> writer, String tableId, SeaTunnelRow row, Throwable t) {
        String effectiveTableId = tableId != null ? tableId : resolveTableId(row);

        if (!isRowError(writer, row, t)) {
            // System-level error: let caller treat it as fatal.
            log.warn(
                    "Multi-table sink encountered non-row-level error in plugin [{}], table [{}]: {}",
                    pluginName,
                    effectiveTableId,
                    t != null ? t.getMessage() : null,
                    t);
            return false;
        }

        RowErrorContext ctx = new RowErrorContext("SINK", "SINK", pluginName, effectiveTableId);

        // Delegate to shared ErrorHandler; it may throw when thresholds or queue policies demand
        // a job failure.
        log.debug(
                "Routing multi-table row-level error to error handler for plugin [{}], table [{}]: {}",
                pluginName,
                effectiveTableId,
                t != null ? t.getMessage() : null);
        ErrorHandler.ErrorHandleResult result = errorHandler.onError(ctx, row, t);

View on GitHub (pinned to cf67b549a7)