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), nil

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Resolve the collation before coercing: use the connection/environment default collation when mergeAndCoerceCollations yields Unknown
  2. Ensure the query's charset/collation is supported by Vitess (check vtgate's collation environment and the column's DDL)
  3. 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

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


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