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

  1. Reload the table schema and restart the stream (fresh copy) so the cached Fields match the current row format
  2. Pause binlog streaming during ALTERs, or use vitess's schema-tracking (wait for the migration position before streaming)
  3. Ensure the schema engine (bls.se) cache is invalidated on DDL events in the stream
  4. 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

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


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