risingwavelabs/risingwave · error
received a DDL message, please set `canal.instance.filter.qu
Error message
received a DDL message, please set `canal.instance.filter.query.dml` to true.
What it means
The Canal JSON parser refuses messages whose `isDdl` flag is true. RisingWave consumes only DML change events; DDL statements must be filtered at the Canal server so they never appear on the Kafka topic.
Source
Thrown at src/connector/src/parser/canal/simd_json_parser.rs:71
})
}
#[expect(clippy::unused_async)]
pub async fn parse_inner(
&self,
mut payload: Vec<u8>,
mut writer: SourceStreamChunkRowWriter<'_>,
) -> ConnectorResult<()> {
let mut event: BorrowedValue<'_> =
simd_json::to_borrowed_value(&mut payload[self.payload_start_idx..])
.context("failed to parse canal json payload")?;
let is_ddl = event
.get(IS_DDL)
.and_then(|v| v.as_bool())
.context("field `isDdl` not found in canal json")?;
if is_ddl {
bail!("received a DDL message, please set `canal.instance.filter.query.dml` to true.");
}
let op = match event.get(OP).and_then(|v| v.as_str()) {
Some(CANAL_INSERT_EVENT | CANAL_UPDATE_EVENT) => ChangeEventOperation::Upsert,
Some(CANAL_DELETE_EVENT) => ChangeEventOperation::Delete,
_ => bail!("op field not found in canal json"),
};
let events = event
.get_mut(DATA)
.and_then(|v| match v {
BorrowedValue::Array(array) => Some(array),
_ => None,
})
.context("field `data` is missing for creating event")?;
let mut errors = Vec::new();
for event in events.drain(..) {View on GitHub (pinned to 6469eb736d)
Solutions
- Set `canal.instance.filter.query.dml=true` in the Canal instance configuration and restart the instance
- Filter the topic on the producer side (e.g. Canal MQ routing rules) to drop DDL messages
- Use a separate Canal topic/instance for DDL if DDL capture is needed elsewhere
Example fix
// canal.properties / instance.properties (before) # canal.instance.filter.query.dml unset // after canal.instance.filter.query.dml=true
Defensive patterns
Strategy: validation
Validate before calling
if (msg && msg.isDdl === true) { /* route to DDL handler or drop before feeding RW */ } Type guard
const isDml = (m) => m && m.isDdl === false && typeof m.op === 'string';
Try / catch
if err.to_string().contains("DDL message") { drop_event_and_metric("canal_ddl_filtered"); } else { propagate } Prevention
- Always set canal.instance.filter.query.dml=true for CDC sources feeding RisingWave
- Test the Canal instance config with a dummy DDL before production
- Monitor the topic for DDL events with a side consumer
When it happens
Trigger: A Canal instance was configured without `canal.instance.filter.query.dml=true`, so DDL events (CREATE/ALTER/DROP) are serialized into the topic and parsed by parse_inner.
Common situations: Default Canal deployments that don't filter DDL; adding DDL to a replicated database after the source was set up; sharing a Canal topic between consumers where one needs DDL.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- op field not found in canal json
- failed to parse {} row(s) in a single canal json message: {}
- no value found at column: {}, index: {}
- failed to deserialize MySQL value into rust value
- failed to deserialize MySQL value into rw value
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/d7ac18cf81b46c30.
Report an issue: GitHub.