vitessio/vitess · error

unknown tableID %v in WriteRows event

Error message

unknown tableID %v in WriteRows event

What it means

A WriteRowsEvent carries rows identified only by a numeric table_id that must have been declared by a preceding TABLE_MAP_EVENT. If tableMaps has no entry for that ID, the streamer cannot identify the table and aborts the stream.

Source

Thrown at go/vt/binlog/binlog_streamer.go:521

			if bls.extractPK {
				tce.pkNames = make([]*querypb.Field, len(tce.ti.PKColumns))
				tce.pkIndexes = make([]int, len(tce.ti.Fields))
				for i := range tce.pkIndexes {
					// Put -1 as default in here.
					tce.pkIndexes[i] = -1
				}
				for i, c := range tce.ti.PKColumns {
					// Patch in every PK column index.
					tce.pkIndexes[c] = i
					// Fill in pknames
					tce.pkNames[i] = tce.ti.Fields[c]
				}
			}
		case ev.IsWriteRows():
			tableID := ev.TableID(format)
			tce, ok := tableMaps[tableID]
			if !ok {
				return pos, fmt.Errorf("unknown tableID %v in WriteRows event", tableID)
			}
			if tce.ti == nil {
				// Skip cross-db statements.
				continue
			}
			setTimestamp := &binlogdatapb.BinlogTransaction_Statement{
				Category: binlogdatapb.BinlogTransaction_Statement_BL_SET,
				Sql:      fmt.Appendf(nil, "SET TIMESTAMP=%d", ev.Timestamp()),
			}
			statements = append(statements, FullBinlogStatement{
				Statement: setTimestamp,
			})

			rows, err := ev.Rows(format, tce.tm)
			if err != nil {
				return pos, err
			}

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Restart the stream from a clean transaction boundary (a GTID/position before the transaction) so TABLE_MAP and rows events stay paired
  2. Validate the binlog file with mysqlbinlog to find corruption or missing table-map events
  3. If filtering databases, ensure filtering never drops TABLE_MAP events whose rows events are still streamed
  4. Fix custom event generators to emit TABLE_MAP before rows events
Defensive patterns

Strategy: validation

Validate before calling

// ensure stream starts at a transaction boundary:
// mysqlbinlog --start-position=<pos> <file> | head -50
// confirm a Table_map event precedes the Write_rows event

Try / catch

err := streamer.Stream(ctx, signals)
if err != nil && strings.Contains(err.Error(), "unknown tableID") {
    // restart from a clean GTID/transaction boundary
}

Prevention

When it happens

Trigger: In parseEvents, ev.IsWriteRows() yields a tableID not present in the tableMaps built from prior TABLE_MAP events — the map event was skipped (wrong db filter), lost, or the rows event is corrupt/misaligned.

Common situations: Stream started mid-transaction so the TABLE_MAP was missed; binlog file corruption causing event misalignment; custom binlog emitters writing rows without a matching table map; cross-database replication where the map event was filtered out.

Related errors


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