vitessio/vitess · error
WeightStringLen called with non-MOD4 length
Error message
WeightStringLen called with non-MOD4 length
What it means
Collation_utf8mb4_uca_0900.WeightStringLen computes the maximum weight-string size for a UCA-900 collation. It assumes the input is a UTF-8 string whose byte length is a multiple of 4 (it divides by 4 to get codepoint count for the worst case of 3-byte codepoints... caller contract requires MOD4). If numBytes is not divisible by 4, the sizing arithmetic is invalid for this collation's expectations, so it panics rather than returning a wrong buffer size.
Source
Thrown at go/mysql/collations/colldata/uca.go:216
}
hasher.Write(chunk[:16])
}
hasher.Write(chunk[:n])
return
}
for {
w, ok := it.Next()
if !ok {
break
}
hasher.Write16(bits.ReverseBytes16(w))
}
}
func (c *Collation_utf8mb4_uca_0900) WeightStringLen(numBytes int) int {
if numBytes%4 != 0 {
panic("WeightStringLen called with non-MOD4 length")
}
levels := int(c.uca.MaxLevel())
weights := (numBytes / 4) * uca.MaxCollationElementsPerCodepoint * levels
weights += levels - 1 // one NULL byte as a separator between levels
return weights * 2 // two bytes per weight
}
func (c *Collation_utf8mb4_uca_0900) Wildcard(pat []byte, matchOne rune, matchMany rune, escape rune) WildcardPattern {
// The matcher must not take the Collate shortcut for literal patterns:
// MySQL compares LIKE character by character and does not apply the
// expansions and contractions that Collate applies, so 'ß' = 'ss' is
// true and 'ß' LIKE 'ss' is false.
return newUnicodeWildcardMatcher(charset.Charset_utf8mb4{}, c.uca.WeightsEqual, nil, pat, matchOne, matchMany, escape)
}
func (c *Collation_utf8mb4_uca_0900) ToLower(dst, src []byte) []byte {
dst = append(dst, bytes.ToLower(src)...)
return dstView on GitHub (pinned to 01a25a7d17)
Solutions
- Ensure the byte length passed is a multiple of 4 (pad or use the full utf8mb4 string length); for arbitrary strings use WeightString instead of WeightStringLen
- Verify the data really is utf8mb4; if it may be truncated, use a rune-safe length (utf8.RuneCountInString * 4 rounded appropriately) before calling
- If calling from engine code, only call WeightStringLen for exact-typing paths where length%4==0 is guaranteed (e.g. CHAR(4n) columns); otherwise fall back to the generic sizing path
Example fix
// before
n := coll.WeightStringLen(len(b)) // panics when len(b)%4 != 0
// after
if len(b)%4 != 0 {
// fall back to actual weight string computation
w := make([]byte, coll.WeightStringMaxLen(len(b)))
coll.WeightString(b, w)
} else {
n := coll.WeightStringLen(len(b))
} Defensive patterns
Strategy: validation
Validate before calling
if len(input)%4 != 0 {
// do not call WeightStringLen; pad to a codepoint boundary or use WeightString
} Type guard
func isMod4(n int) bool { return n%4 == 0 } Prevention
- Only call WeightStringLen on guaranteed utf8mb4 CHAR-typed data
- Prefer WeightString/WeightStringMaxLen for arbitrary-length inputs
- Never truncate utf8mb4 buffers mid-codepoint before sizing
When it happens
Trigger: Calling WeightStringLen(numBytes) on a Collation_utf8mb4_uca_0900 with numBytes not a multiple of 4 — typically passing a byte length from a non-utf8mb4 string, a truncated UTF-8 buffer, or a binary/varbinary value.
Common situations: Computing buffer sizes for weight strings on hand-built queries or binary columns routed to utf8mb4_uca_0900 collations; truncating string data mid-codepoint before hashing; migrating code from latin1 where byte lengths are never MOD4.
Related errors
- unreachable
- len(p.Patch)%3
- cluster type can be only one of vitess or mysql
- this command panics on purpose
- no function closure for Event stream specified
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/0548a28add9c56ff.
Report an issue: GitHub.