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

  1. Ensure the source MySQL servers use binlog_format=ROW (not STATEMENT or MIXED)
  2. Upgrade Vitess so the sqlparser/analyzer recognizes the statement type being replicated
  3. Identify the offending SQL from the error payload and avoid or rewrite that statement on the primary
  4. 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

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


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/f2d3658e4e484d1c. Report an issue: GitHub.