vitessio/vitess · error

bad encoding

Error message

bad encoding

What it means

NewTableMapEvent panics with "bad encoding" as a self-check after serializing the table-map event: the number of bytes written into the buffer does not match the pre-computed buffer length. This indicates an internal inconsistency in the metadata encoding (e.g. a column type whose metadata size was computed differently than written).

Source

Thrown at go/mysql/binlog_event_make.go:380

	pos := 6 + 2 + 1 + copy(data[9:], tm.Database)
	data[pos] = 0
	pos++
	data[pos] = byte(len(tm.Name))
	pos += 1 + copy(data[pos+1:], tm.Name)
	data[pos] = 0
	pos++

	pos = writeLenEncInt(data, pos, uint64(len(tm.Types)))
	pos += copy(data[pos:], tm.Types)

	pos = writeLenEncInt(data, pos, uint64(metadataLength))
	for c, typ := range tm.Types {
		pos = metadataWrite(data, pos, typ, tm.Metadata[c])
	}

	pos += copy(data[pos:], tm.CanBeNull.data)
	if pos != len(data) {
		panic("bad encoding")
	}

	ev := s.Packetize(f, eTableMapEvent, 0, data)
	return NewMariadbBinlogEvent(ev)
}

// NewRowsQueryEvent returns a ROWS_QUERY_LOG_EVENT containing the given SQL query
// which is encoded as a NULL byte terminated string.
func NewRowsQueryEvent(f BinlogFormat, s *FakeBinlogStream, query string) BinlogEvent {
	length := 1 + // post-header (always 1 byte)
		len(query) + // SQL query
		1 // NULL byte terminator
	data := make([]byte, length)
	data[0] = 1 // post-header length byte which is ignored
	copy(data[1:], query)
	// data[length-1] is already 0 (NULL byte terminator)

	ev := s.Packetize(f, eRowsQueryEvent, 0, data)

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Use only supported MySQL column types in TableMap.Types.
  2. Ensure Metadata entries correspond one-to-one with Types and use the expected widths.
  3. If a new type is needed, update metadataLength, metadataWrite, and metadataRead consistently.

Example fix

// before
tm.Types = []byte{0xF9 /* unsupported synthetic */}
ev := mysql.NewTableMapEvent(f, s, id, tm) // panics: bad encoding
// after
tm.Types = []byte{binlog.TypeVarchar}
tm.Metadata = []uint16{uint16(len(colDef))}
ev := mysql.NewTableMapEvent(f, s, id, tm)
Defensive patterns

Strategy: validation

Validate before calling

for _, typ := range tm.Types {
    switch typ {
    case binlog.TypeVarchar, binlog.TypeBit, binlog.TypeVarString, /* ...supported... */ :
    default:
        return errors.Errorf("unsupported column type %d in TableMap", typ)
    }
}

Try / catch

func safeTableMapEvent(f mysql.BinlogFormat, s *mysql.FakeBinlogStream, id uint64, tm *mysql.TableMap) (ev mysql.BinlogEvent, err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("NewTableMapEvent: %v", r)
        }
    }()
    return mysql.NewTableMapEvent(f, s, id, tm), nil
}

Prevention

When it happens

Trigger: Calling NewTableMapEvent with a TableMap whose Types include a type that metadataLength and metadataWrite size differently, or with inconsistent Metadata/CanBeNull data so pos != len(data) after serialization.

Common situations: Tests using synthetic column types (types the metadata functions don't recognize); hand-built TableMap structs with mismatched Types/Metadata arrays; changes to the type list without updating both size computation and writer.

Related errors


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