apache/flink · error · IOException

Unknown "op" value "%s". The Debezium Avro message is '%s'

Error message

Unknown "op" value "%s". The Debezium Avro message is '%s'

What it means

Thrown when the 'op' field of a Debezium Avro message is not one of the recognized values ('r' read, 'c' create, 'u' update, 'd' delete) handled in DebeziumAvroDeserializationSchema.deserialize(). It signals the payload on the topic is not a well-formed Debezium envelope — either a different producer wrote Avro records to the topic, the schema was hand-rolled with wrong enum values, or the wrong schema/subject is being resolved. The full raw message is included to aid diagnosis.

Source

Thrown at flink-formats/flink-avro-confluent-registry/src/main/java/org/apache/flink/formats/avro/registry/confluent/debezium/DebeziumAvroDeserializationSchema.java:161

                out.collect(after);
            } else if (OP_UPDATE.equals(op)) {
                if (before == null) {
                    throw new IllegalStateException(
                            String.format(REPLICA_IDENTITY_EXCEPTION, "UPDATE"));
                }
                before.setRowKind(RowKind.UPDATE_BEFORE);
                after.setRowKind(RowKind.UPDATE_AFTER);
                out.collect(before);
                out.collect(after);
            } else if (OP_DELETE.equals(op)) {
                if (before == null) {
                    throw new IllegalStateException(
                            String.format(REPLICA_IDENTITY_EXCEPTION, "DELETE"));
                }
                before.setRowKind(RowKind.DELETE);
                out.collect(before);
            } else {
                throw new IOException(
                        format(
                                "Unknown \"op\" value \"%s\". The Debezium Avro message is '%s'",
                                op, new String(message)));
            }
        } catch (Throwable t) {
            // a big try catch to protect the processing.
            throw new IOException("Can't deserialize Debezium Avro message.", t);
        }
    }

    @Override
    public boolean isEndOfStream(RowData nextElement) {
        return false;
    }

    @Override
    public TypeInformation<RowData> getProducedType() {
        return producedTypeInfo;

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Inspect the failing raw message (included in the exception text) to see the actual 'op' value and envelope shape.
  2. Verify the topic only contains Debezium-produced Avro events: kcat -C -t <topic> -p 0 with the registry Avro decoder.
  3. Check the schema registry subject strategy and that the deserializer resolves the subject Debezium writes (default <topic>-value).
  4. If a custom producer is involved, fix it to emit the Debezium envelope with op in {r,c,u,d} and matching field names.
Defensive patterns

Strategy: validation

Validate before calling

-- Before binding the table, sample the topic and assert op values:
-- kcat -C -t <topic> -o -20 -s value=avro -r http://registry:8081 | jq -e '.payload.op | test("^[rcud]$")'
-- Non-zero exit => topic is not a valid Debezium envelope; do not start the job.

Try / catch

catch (IOException e) { log raw message from exception text; quarantine the topic/offset and stop — this is a data-shape bug, retries will not help }

Prevention

When it happens

Trigger: Pointing 'debezium-avro-confluent' format at a plain Avro topic or a topic produced by a non-Debezium producer; a custom Avro writer whose 'op' field uses values like 'UPDATE' instead of 'u'; schema registry resolving to the wrong subject (auto-registration naming mismatch) so the reader decodes bytes against an unrelated schema; truncated/corrupt records decoded into garbage.

Common situations: Topic naming drift after Debezium connector rename; mixing Debezium topics and application Avro topics under the same subject strategy; using value subjects in place of key subjects; a Confluent registry with RECORD_NAME strategy merging incompatible schemas.

Related errors


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