apache/flink · error · UnsupportedOperationException

Unsupported operation '%s' for row kind.

Error message

Unsupported operation '%s' for row kind.

What it means

Thrown by CanalJsonSerializationSchema.rowKind2String when the row's RowKind is not INSERT, UPDATE_AFTER, UPDATE_BEFORE, or DELETE. Canal-JSON can only express INSERT ('INSERT') and DELETE ('DELETE') operations, so other row kinds have no mapping. Standard Flink RowKinds never hit this; it requires a custom or unusual RowKind value.

Source

Thrown at flink-formats/flink-json/src/main/java/org/apache/flink/formats/json/canal/CanalJsonSerializationSchema.java:102

            ArrayData arrayData = new GenericArrayData(new RowData[] {row});
            reuse.setField(0, arrayData);
            reuse.setField(1, opType);
            return jsonSerializer.serialize(reuse);
        } catch (Throwable t) {
            throw new RuntimeException("Could not serialize row '" + row + "'.", t);
        }
    }

    private StringData rowKind2String(RowKind rowKind) {
        switch (rowKind) {
            case INSERT:
            case UPDATE_AFTER:
                return OP_INSERT;
            case UPDATE_BEFORE:
            case DELETE:
                return OP_DELETE;
            default:
                throw new UnsupportedOperationException(
                        "Unsupported operation '" + rowKind + "' for row kind.");
        }
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        CanalJsonSerializationSchema that = (CanalJsonSerializationSchema) o;
        return Objects.equals(jsonSerializer, that.jsonSerializer);
    }

    @Override
    public int hashCode() {

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Normalize RowKinds to the four standard kinds before the canal-json sink (e.g. map truncate to DELETE+INSERT pairs or filter)
  2. If custom verbs are required, switch the sink format to one that carries richer metadata (changelog-json style or raw JSON with a verb column)
  3. Guard the sink with a filter/mapping function that rejects or converts nonstandard kinds

Example fix

// before: custom kind flows into canal-json sink
RowData out = new GenericRowData(RowKind.of((short) 5), ...); // nonstandard

// after: normalize to a supported kind first
RowData out = new GenericRowData(RowKind.DELETE, ...);
Defensive patterns

Strategy: validation

Validate before calling

RowKind k = row.getRowKind();
if (k != RowKind.INSERT && k != RowKind.UPDATE_BEFORE && k != RowKind.UPDATE_AFTER && k != RowKind.DELETE) {
    throw new IllegalArgumentException("canal-json cannot encode " + k);
}

Type guard

static boolean canalEncodable(RowKind k) {
    return k == RowKind.INSERT || k == RowKind.UPDATE_BEFORE
        || k == RowKind.UPDATE_AFTER || k == RowKind.DELETE;
}

Prevention

When it happens

Trigger: A RowData carrying a RowKind outside the four standard kinds — e.g. a custom RowKind created via new RowKind(short) or an edit stream with additional verbs — being serialized with format 'canal-json'.

Common situations: Custom RowKind definitions in advanced CDC pipelines; upstream edit streams mapping extra verbs (truncate, upsert-special) onto nonstandard kinds; library code reusing RowKind ordinals incompatibly across versions.

Related errors


AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14). Data as JSON: /api/errors/ca83c780d823fdb6. Report an issue: GitHub.