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 dst

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. 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
  2. Verify the data really is utf8mb4; if it may be truncated, use a rune-safe length (utf8.RuneCountInString * 4 rounded appropriately) before calling
  3. 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

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


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