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
- Use only supported MySQL column types in TableMap.Types.
- Ensure Metadata entries correspond one-to-one with Types and use the expected widths.
- 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
- Keep Types, Metadata, and CanBeNull arrays the same length and mutually consistent.
- Only use binlog.Type* constants in TableMap.Types.
- Update metadataLength and metadataWrite together when adding types.
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
- Not implemented, post_header_length!=8
- Not implemented, post_header_length==6
- metadataLength: unhandled data type: %v
- metadataRead: unhandled data type: %v
- GetPreviousGTIDs: previous GTIDs not found
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/103bb714c1734526.
Report an issue: GitHub.