vitessio/vitess · error
SBR mode unsupported for streaming: %s
Error message
SBR mode unsupported for streaming: %s
What it means
During keyrange-filtered binlog streaming, an INSERT/UPDATE/DELETE statement has no KeyspaceID attached, meaning it came from statement-based replication (SBR) or lacked row metadata. Keyrange filtering requires row-based replication so each row's keyspace ID can be compared against the requested keyrange.
Source
Thrown at go/vt/binlog/keyrange_filter.go:50
// passed into the Streamer: bls.Stream(file, pos, sendTransaction) ->
// bls.Stream(file, pos, keyRangeFilterFunc(keyrange, sendTransaction))
func keyRangeFilterFunc(keyrange *topodatapb.KeyRange, callback func(*binlogdatapb.BinlogTransaction) error) sendTransactionFunc {
return func(eventToken *querypb.EventToken, statements []FullBinlogStatement) error {
matched := false
filtered := make([]*binlogdatapb.BinlogTransaction_Statement, 0, len(statements))
for _, statement := range statements {
switch statement.Statement.Category {
case binlogdatapb.BinlogTransaction_Statement_BL_SET:
filtered = append(filtered, statement.Statement)
case binlogdatapb.BinlogTransaction_Statement_BL_DDL:
log.Warn(fmt.Sprintf("Not forwarding DDL: %s", statement.Statement.Sql))
continue
case binlogdatapb.BinlogTransaction_Statement_BL_INSERT,
binlogdatapb.BinlogTransaction_Statement_BL_UPDATE,
binlogdatapb.BinlogTransaction_Statement_BL_DELETE:
if statement.KeyspaceID == nil {
updateStreamErrors.Add("KeyRangeStream", 1)
return fmt.Errorf("SBR mode unsupported for streaming: %s", statement.Statement.Sql)
}
if !key.KeyRangeContains(keyrange, statement.KeyspaceID) {
continue
}
filtered = append(filtered, statement.Statement)
matched = true
case binlogdatapb.BinlogTransaction_Statement_BL_UNRECOGNIZED:
updateStreamErrors.Add("KeyRangeStream", 1)
log.Error(fmt.Sprintf("Error parsing keyspace id: %s", statement.Statement.Sql))
continue
}
}
trans := &binlogdatapb.BinlogTransaction{
EventToken: eventToken,
}
if matched {
trans.Statements = filtered
}View on GitHub (pinned to 01a25a7d17)
Solutions
- Set binlog_format=ROW on the source MySQL server and restart/repick the streamer.
- Identify and rewrite the offending statement (shown in the error) so it replicates row-based.
- If the statement is intentionally SBR, do not request keyrange-filtered streaming for that workload.
Example fix
-- before SET GLOBAL binlog_format = 'STATEMENT'; -- after SET GLOBAL binlog_format = 'ROW';
Defensive patterns
Strategy: validation
Validate before calling
// require RBR before keyrange streaming
out, _ := exec.Command("mysql", "-e", "SELECT @@binlog_format").Output()
if !strings.Contains(string(out), "ROW") {
return errors.New("binlog_format must be ROW for keyrange streaming")
} Try / catch
if err := bls.StreamKeyRange(ctx, ...); err != nil {
if strings.Contains(err.Error(), "SBR mode unsupported") {
return fmt.Errorf("switch source to binlog_format=ROW: %w", err)
}
return err
} Prevention
- Always run binlog_format=ROW on replication sources used for keyrange streaming.
- Avoid SBR-fallback statements (e.g. some DDL-ish/DML mixes) on streamed tables.
- Audit statements flagged in the error and rewrite them for row-based replication.
When it happens
Trigger: Streaming a binlog to a KeyRange while the source MySQL uses binlog_format=STATEMENT, or a DML statement carries no KeyspaceID in the BinlogTransaction statement.
Common situations: Mixed RBR/SBR deployments where some statements fall back to SBR (e.g. statements that MySQL can't replicate row-based, or binlog_format not set to ROW on the source).
Related errors
- stream error @ (including the GTID we failed to process) %v:
- send reply error: %v
- can't parse RAND_EVENT: %v, event data: %#v
- can't get query from binlog event: %v, event data: %#v
- newKeyspaceIDResolverFactory failed: %v
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/ef0359f478b58886.
Report an issue: GitHub.