vitessio/vitess · error
EvalResult.coerce with no collation
Error message
EvalResult.coerce with no collation
What it means
evalCoerce converts an evaluated value to a target type, and character coercions need a concrete collation ID to pick the target charset. If col == collations.Unknown it panics because coercing to a string type without knowing the collation is meaningless. It is an internal guard: callers must resolve a real collation (or not request a string coercion) before calling evalCoerce.
Source
Thrown at go/vt/vtgate/evalengine/eval.go:180
return makeboolean(bit.i != 0)
}
f, _ := fastparse.ParseFloat64(e.string())
return makeboolean(f != 0.0)
case *evalJSON:
return makeboolean(e.ToBoolean())
case *evalTemporal:
return makeboolean(!e.isZero())
default:
panic("unhandled case: evalIsTruthy")
}
}
func evalCoerce(e eval, typ sqltypes.Type, size, scale int32, col collations.ID, now time.Time, allowZero bool) (eval, error) {
if e == nil {
return nil, nil
}
if col == collations.Unknown {
panic("EvalResult.coerce with no collation")
}
if typ == sqltypes.VarChar || typ == sqltypes.Char {
// if we have an explicit VARCHAR coercion, always force it so the collation is replaced in the target
return evalToVarchar(e, col, false)
}
if e.SQLType() == typ && e.Size() == size && e.Scale() == scale {
// nothing to be done here
return e, nil
}
switch typ {
case sqltypes.Null:
return nil, nil
case sqltypes.Binary, sqltypes.VarBinary:
return evalToBinary(e), nil
case sqltypes.Char, sqltypes.VarChar:
panic("unreacheable")
case sqltypes.Decimal:
return evalToDecimal(e, 0, 0), nilView on GitHub (pinned to 01a25a7d17)
Solutions
- Resolve the collation before coercing: use the connection/environment default collation when mergeAndCoerceCollations yields Unknown
- Ensure the query's charset/collation is supported by Vitess (check vtgate's collation environment and the column's DDL)
- If writing engine code, guard the call: only pass Unknown for non-string target types, and return a vterrors error instead of reaching this panic
Example fix
// before return evalCoerce(e, sqltypes.VarChar, size, 0, collations.Unknown, now, false) // after col := env.DefaultCollation() // or resolved merged collation return evalCoerce(e, sqltypes.VarChar, size, 0, col, now, false)
Defensive patterns
Strategy: validation
Validate before calling
// Go: resolve a concrete collation before coercion
if col == collations.Unknown {
col = env.DefaultCollation()
}
return evalCoerce(e, typ, size, scale, col, now, allowZero) Type guard
func hasCollation(col collations.ID) bool {
return col != collations.Unknown && colldata.Lookup(col) != nil
} Try / catch
func safeCoerce(e eval, typ sqltypes.Type, col collations.ID) (out eval, err error) {
defer func() {
if r := recover(); r != nil {
err = vterrors.Errorf(vtrpcpb.Code_INTERNAL, "coercion failed: %v", r)
}
}()
return evalCoerce(e, typ, 0, 0, col, time.Now(), false)
} Prevention
- Always run mergeAndCoerceCollations (or fall back to the connection default) before string coercion
- Validate column DDL collations are supported by the Vitess collation environment at deploy time
- Return vterrors with Code_UNKNOWN for unsupported collations instead of propagating collations.Unknown
When it happens
Trigger: Calling evalCoerce with col=collations.Unknown while typ (or the value's coercion path) resolves to a character type; e.g. building a coercion plan where the collation merge step was skipped or returned Unknown for an unsupported/invalid collation.
Common situations: Queries involving columns or literals with collations unsupported by the configured collation environment, hitting paths that pass collations.Unknown downstream into evalCoerce; also hit by plugin/extension code constructing coercions manually.
Related errors
- unreacheable
- called EvalResult.truncate on non-quoted
- malformed hex literal from parser
- bad unsigned integer type
- bad type aggregation for signed/unsigned types
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/a2d4431c25a3dc0b.
Report an issue: GitHub.