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
- Inspect the failing raw message (included in the exception text) to see the actual 'op' value and envelope shape.
- Verify the topic only contains Debezium-produced Avro events: kcat -C -t <topic> -p 0 with the registry Avro decoder.
- Check the schema registry subject strategy and that the deserializer resolves the subject Debezium writes (default <topic>-value).
- 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
- Never mix Debezium and non-Debezium producers on one topic
- Pin the registry subject strategy so readers resolve the exact Debezium subject
- Add a staging consumer that validates the envelope before production jobs bind
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
- Failed to serialize schema registry.
- Unknown data format. Magic number does not match
- Could not find schema with id %s in registry
- Can't deserialize Debezium Avro message.
- Unsupported operation '%s' for row kind.
AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14).
Data as JSON: /api/errors/8d5a68d1d57b6af0.
Report an issue: GitHub.