vitessio/vitess · error

UnicodeLooseMD5.Map: %v

Error message

UnicodeLooseMD5.Map: %v

What it means

UnicodeLooseMD5.Map maps each id to a shard DestinationKeyspaceID by hashing it with the collation-normalized MD5; it wraps any Hash failure in 'UnicodeLooseMD5.Map: %v'. The underlying unicodeHash fails only if the value cannot be converted to bytes or contains invalid UTF-8. This error aborts routing: VTGate cannot determine the destination shard for the offending row.

Source

Thrown at go/vt/vtgate/vindexes/unicodeloosemd5.go:91

func (vind *UnicodeLooseMD5) Verify(ctx context.Context, vcursor VCursor, ids []sqltypes.Value, ksids [][]byte) ([]bool, error) {
	out := make([]bool, 0, len(ids))
	for i, id := range ids {
		data, err := vind.Hash(id)
		if err != nil {
			return nil, fmt.Errorf("UnicodeLooseMD5.Verify: %v", err)
		}
		out = append(out, bytes.Equal(data, ksids[i]))
	}
	return out, nil
}

// Map can map ids to key.ShardDestination objects.
func (vind *UnicodeLooseMD5) Map(ctx context.Context, vcursor VCursor, ids []sqltypes.Value) ([]key.ShardDestination, error) {
	out := make([]key.ShardDestination, 0, len(ids))
	for _, id := range ids {
		data, err := vind.Hash(id)
		if err != nil {
			return nil, fmt.Errorf("UnicodeLooseMD5.Map: %v", err)
		}
		out = append(out, key.DestinationKeyspaceID(data))
	}
	return out, nil
}

func (vind *UnicodeLooseMD5) Hash(id sqltypes.Value) ([]byte, error) {
	return unicodeHash(&collateMD5, id)
}

// UnknownParams implements the ParamValidating interface.
func (vind *UnicodeLooseMD5) UnknownParams() []string {
	return vind.unknownParams
}

func init() {
	Register("unicode_loose_md5", newUnicodeLooseMD5)
}

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Log/capture the offending value from the failing query and check its encoding with a UTF-8 validator.
  2. Set the client connection charset to utf8mb4 so string literals arrive as valid UTF-8.
  3. Fix or re-encode the bad rows/data at the source before retrying the query.
  4. Change the column charset to utf8mb4 or choose a binary vindex if the data is inherently non-unicode.
  5. If it is a programmatic caller, pass plain string sqltypes.Value values, not tuple/expression values.

Example fix

// before
INSERT INTO t (name) VALUES (UNHEX('FF')); -- invalid UTF-8 hits unicode_loose_md5 vindex
// after
INSERT INTO t (name) VALUES ('café'); -- valid utf8mb4, or switch vindex to binary_md5 for raw bytes
Defensive patterns

Strategy: validation

Validate before calling

if !utf8.ValidString(val) {
    return fmt.Errorf("value %q is not valid UTF-8 for unicode_loose_md5 vindex", val)
}

Try / catch

dest, err := vindex.Map(ctx, vc, ids)
if err != nil {
    log.Warn("map failed", slog.Any("error", err))
    return err // query cannot be routed; surface to client
}

Prevention

When it happens

Trigger: Any query whose WHERE clause / insert row uses the unicode_loose_md5 vindexed column with a value that is not valid UTF-8 or cannot be reduced to bytes (e.g. NULL tuple value, binary-typed bind var).

Common situations: Application sending binary or non-UTF8-encoded strings (wrong connection charset); schema changed column to a binary type; ETL jobs writing legacy-encoded data; tools building sqltypes.Value from raw bytes.

Related errors


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