vitessio/vitess · error
unexpected statement type %s in row-based replication: %q
Error message
unexpected statement type %s in row-based replication: %q
What it means
While streaming row-based replication (RBR), vstreamer encountered a statement-category query event it cannot represent in the row-event stream. Only a known subset of statement types (DDL, and certain filtered/annotated statements) is expected; anything else in a RBR stream is treated as a protocol violation and fails the vstream.
Source
Thrown at go/vt/vttablet/tabletserver/vstreamer/vstreamer.go:728
case sqlparser.StmtOther, sqlparser.StmtAnalyze, sqlparser.StmtPriv, sqlparser.StmtSet, sqlparser.StmtComment, sqlparser.StmtFlush:
// These are either:
// 1) DBA statements like REPAIR that can be ignored.
// 2) Privilege-altering statements like GRANT/REVOKE
// that we want to keep out of the stream for now.
if shouldSend(binlogdatapb.VEventType_GTID) {
vevents = append(vevents, &binlogdatapb.VEvent{
Type: binlogdatapb.VEventType_GTID,
Gtid: replication.EncodePosition(vs.pos),
})
}
// OTHER is the terminal marker for these statements. It must still be
// present internally so parseEvents flushes buffered requested events
// even when OTHER is filtered from the caller's output.
vevents = append(vevents, &binlogdatapb.VEvent{
Type: binlogdatapb.VEventType_OTHER,
})
default:
return nil, fmt.Errorf("unexpected statement type %s in row-based replication: %q", cat, q.SQL)
}
case ev.IsRowsQuery():
if !shouldSend(binlogdatapb.VEventType_ROWS_QUERY) {
return nil, nil
}
query, err := ev.RowsQuery(vs.format)
if err != nil {
return nil, vterrors.Wrapf(err, "failed to parse ROWS_QUERY_LOG_EVENT")
}
vevents = append(vevents, &binlogdatapb.VEvent{
Type: binlogdatapb.VEventType_ROWS_QUERY,
Statement: query,
})
case ev.IsTableMap():
if !shouldSend(binlogdatapb.VEventType_ROW) && !shouldSend(binlogdatapb.VEventType_FIELD) {
return nil, nil
}
// This is very frequent. It precedes every row event.View on GitHub (pinned to 01a25a7d17)
Solutions
- Ensure the source MySQL servers use binlog_format=ROW (not STATEMENT or MIXED)
- Upgrade Vitess so the sqlparser/analyzer recognizes the statement type being replicated
- Identify the offending SQL from the error payload and avoid or rewrite that statement on the primary
- Check binlog row image / annotate event settings for compatibility
Example fix
// before (my.cnf on primary) binlog_format = MIXED // after binlog_format = ROW
Defensive patterns
Strategy: validation
Validate before calling
// On every source server: // SELECT @@global.binlog_format; -- must be ROW
Prevention
- Set binlog_format=ROW globally on all MySQL sources
- Avoid statements Vitess cannot classify in replicated traffic
- Keep Vitess upgraded to support newer MySQL statement types
When it happens
Trigger: parseEvent's switch over statement categories (cat) hits the default case for a query event received while streaming ROW events — e.g. an unrecognized or unsupported SQL statement type delivered as a QUERY_EVENT.
Common situations: binlog_format mixed/STATEMENT rows slipping through on the primary; custom or newer MySQL statement types the running Vitess version doesn't classify; binlog-annotate settings producing unexpected query payloads.
Related errors
- unsupported constraint: %v
- can't parse binlog event: invalid data: %#v
- can't parse FORMAT_DESCRIPTION_EVENT: %v, event data: %#v
- got a real event before FORMAT_DESCRIPTION_EVENT: %#v
- can't strip checksum from binlog event: %v, event data: %#v
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/f2d3658e4e484d1c.
Report an issue: GitHub.