vitessio/vitess · error
[%v] cached columns count[%d] mismatch binglog row [%d]
Error message
[%v] cached columns count[%d] mismatch binglog row [%d]
What it means
writeValuesAsSQL reconciles the cached table schema fields (tce.ti.Fields) against the column bitmap in the binlog row event. If the counts differ — the table was ALTERed after the schema was cached but before rows were decoded — it refuses to guess column mapping and returns this (typo'd 'binglog') error.
Source
Thrown at go/vt/binlog/binlog_streamer.go:740
}
return statements
}
// writeValuesAsSQL is a helper method to print the values as SQL in the
// provided strings.Builder. It also returns the value for the keyspaceIDColumn,
// and the array of values for the PK, if necessary.
func writeValuesAsSQL(sql *sqlparser.TrackedBuffer, tce *tableCacheEntry, rs *mysql.Rows, rowIndex int, getPK bool) (sqltypes.Value, []sqltypes.Value, error) {
valueIndex := 0
data := rs.Rows[rowIndex].Data
pos := 0
var keyspaceIDCell sqltypes.Value
var pkValues []sqltypes.Value
if getPK {
pkValues = make([]sqltypes.Value, len(tce.pkNames))
}
if len(tce.ti.Fields) != rs.DataColumns.Count() {
err := fmt.Errorf("[%v] cached columns count[%d] mismatch binglog row [%d]", tce.ti.Name, len(tce.ti.Fields), rs.DataColumns.Count())
return sqltypes.Value{}, nil, err
}
for c := 0; c < rs.DataColumns.Count(); c++ {
if !rs.DataColumns.Bit(c) {
continue
}
// Print a separator if needed, then print the name.
if valueIndex > 0 {
sql.WriteString(", ")
}
sql.Myprintf("%v", sqlparser.NewIdentifierCI(tce.ti.Fields[c].Name))
sql.WriteByte('=')
if rs.Rows[rowIndex].NullColumns.Bit(valueIndex) {
// This column is represented, but its value is NULL.
sql.WriteString("NULL")View on GitHub (pinned to 01a25a7d17)
Solutions
- Reload the table schema and restart the stream (fresh copy) so the cached Fields match the current row format
- Pause binlog streaming during ALTERs, or use vitess's schema-tracking (wait for the migration position before streaming)
- Ensure the schema engine (bls.se) cache is invalidated on DDL events in the stream
- If intentional mid-ALTER decoding is needed, split the stream at the ALTER position and re-copy
Defensive patterns
Strategy: validation
Validate before calling
ti := schemaEngine.GetTable(sqlparser.NewIdentifierCS(tableName))
if ti != nil && len(ti.Fields) != eventColumnCount {
// reload schema before streaming
schemaEngine.Reload(ctx)
} Try / catch
err := streamer.Stream(ctx, signals)
if err != nil && strings.Contains(err.Error(), "mismatch binglog row") {
// reload schema cache and restart the stream
} Prevention
- Quiesce binlog streaming during ALTERs / gh-ost cutover
- Invalidate schema cache on DDL events
- Use schema tracking so Fields stay in sync with row events
- Split streams at migration boundaries instead of spanning them
When it happens
Trigger: appendInserts or appendUpdates process a rows event whose DataColumns count != len(tce.ti.Fields): the table's column count in the source changed (ADD/DROP COLUMN) or the cached schema is stale/out-of-order relative to the binlog.
Common situations: Online schema change (gh-ost/LHM) running while vreplication streams rows; streamer started before a migration completed so schema snapshot is old; multiple binlog streams mixing pre/post-ALTER row formats.
Related errors
- unknown table %v in schema
- unknown tableID %v in WriteRows event
- unknown tableID %v in UpdateRows event
- unknown tableID %v in DeleteRows event
- cannot find column %v in table %v
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/25e7cfbd86a57a4d.
Report an issue: GitHub.