vitessio/vitess · error
metadataLength: unhandled data type: %v
Error message
metadataLength: unhandled data type: %v
What it means
metadataLength computes the byte size of the optional metadata for a column type; for unrecognized types it panics with a vterrors INTERNAL error, since this path is expected only in tests with synthetic types. Valid production binlog types never reach the default branch.
Source
Thrown at go/mysql/binlog_event_rbr.go:154
case binlog.TypeDecimal, binlog.TypeTiny, binlog.TypeShort, binlog.TypeLong, binlog.TypeNull, binlog.TypeTimestamp, binlog.TypeLongLong, binlog.TypeInt24, binlog.TypeDate, binlog.TypeTime, binlog.TypeDateTime, binlog.TypeYear, binlog.TypeNewDate:
// No data here.
return 0
case binlog.TypeFloat, binlog.TypeDouble, binlog.TypeTimestamp2, binlog.TypeDateTime2, binlog.TypeTime2, binlog.TypeJSON, binlog.TypeTinyBlob, binlog.TypeMediumBlob, binlog.TypeLongBlob, binlog.TypeBlob, binlog.TypeGeometry, binlog.TypeVector:
// One byte.
return 1
case binlog.TypeNewDecimal, binlog.TypeEnum, binlog.TypeSet, binlog.TypeString:
// Two bytes, Big Endian because of crazy encoding.
return 2
case binlog.TypeVarchar, binlog.TypeBit, binlog.TypeVarString:
// Two bytes, Little Endian
return 2
default:
// Unknown type. This is used in tests only, so panic.
panic(vterrors.Errorf(vtrpcpb.Code_INTERNAL, "metadataLength: unhandled data type: %v", typ))
}
}
// metadataTotalLength returns the total size of the metadata for an
// array of types.
func metadataTotalLength(types []byte) int {
sum := 0
for _, t := range types {
sum += metadataLength(t)
}
return sum
}
// metadataRead reads a single value from the metadata string.
func metadataRead(data []byte, pos int, typ byte) (uint16, int, error) {
switch typ {
case binlog.TypeDecimal, binlog.TypeTiny, binlog.TypeShort, binlog.TypeLong, binlog.TypeNull, binlog.TypeTimestamp, binlog.TypeLongLong, binlog.TypeInt24, binlog.TypeDate, binlog.TypeTime, binlog.TypeDateTime, binlog.TypeYear, binlog.TypeNewDate:
// No data here.View on GitHub (pinned to 01a25a7d17)
Solutions
- Replace the unsupported type byte with a valid binlog.Type* constant.
- Add the missing type to metadataLength (and the matching read/write functions) if it is a real new MySQL type.
- Sanitize the TableMap before building events, rejecting unknown type codes with a proper error.
Example fix
// before
tm.Types = []byte{0x99} // unknown
ev := mysql.NewTableMapEvent(f, s, id, tm) // panics
// after
tm.Types = []byte{binlog.TypeFloat}
ev := mysql.NewTableMapEvent(f, s, id, tm) Defensive patterns
Strategy: validation
Validate before calling
for _, typ := range tm.Types {
if !isKnownBinlogType(typ) {
return errors.Errorf("unknown column type %d before building table map", typ)
}
} Try / catch
func safeMetadataTotal(types []byte) (n int, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("metadataLength: %v", r)
}
}()
return mysql.MetadataTotalLength(types), nil
} Prevention
- Whitelist column types against binlog.Type* constants before constructing events.
- When new MySQL versions add types, extend metadataLength/metadataWrite/metadataRead in the same change.
- In tests, avoid inventing type bytes; reuse constants from the binlog package.
When it happens
Trigger: Calling NewTableMapEvent (via metadataTotalLength -> metadataLength) with a TableMap.Types entry containing a byte that is not a supported MySQL column type.
Common situations: Unit tests using made-up type bytes; corrupted or truncated binlog data supplying invalid type codes; new upstream MySQL types not yet added to the switch.
Related errors
- metadataRead: unhandled data type: %v
- Not implemented, post_header_length!=8
- bad encoding
- Not implemented, post_header_length==6
- GetPreviousGTIDs: previous GTIDs not found
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/ca4d2a0c735be851.
Report an issue: GitHub.