vitessio/vitess · error
unreacheable
Error message
unreacheable
What it means
In evalCoerce, Char/VarChar coercions are handled earlier by an explicit evalToVarchar branch (which also swaps in the target collation). The later switch therefore treats a Char/VarChar target as unreachable and panics. Reaching it means control flow changed — e.g. the early branch was bypassed or a caller coerces to Char/VarChar through a path that no longer routes through the early return.
Source
Thrown at go/vt/vtgate/evalengine/eval.go:196
}
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
case sqltypes.Float32, sqltypes.Float64:
f, _ := evalToFloat(e)
return f, nil
case sqltypes.Int8, sqltypes.Int16, sqltypes.Int32, sqltypes.Int64:
return evalToInt64(e), nil
case sqltypes.Uint8, sqltypes.Uint16, sqltypes.Uint32, sqltypes.Uint64:
return evalToInt64(e).toUint64(), nil
case sqltypes.Date:
return evalToDate(e, now, allowZero), nil
case sqltypes.Datetime, sqltypes.Timestamp:
return evalToDateTime(e, int(size), now, allowZero), nil
case sqltypes.Time:
return evalToTime(e, int(size)), nil
default:
return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "Unsupported type conversion: %s", typ.String())
}View on GitHub (pinned to 01a25a7d17)
Solutions
- Route the call through the explicit VARCHAR coercion path (evalToVarchar) instead of the generic switch
- If the early branch was refactored, restore handling for Char/VarChar in the switch by delegating to evalToVarchar(e, col, false)
- Add a regression test coercing to VarChar/Char to cover the path
Example fix
// before
case sqltypes.Char, sqltypes.VarChar:
panic("unreacheable")
// after
case sqltypes.Char, sqltypes.VarChar:
return evalToVarchar(e, col, false), nil Defensive patterns
Strategy: type-guard
Validate before calling
// Go: only call evalCoerce's switch for non-CHAR/VARCHAR targets
if typ == sqltypes.Char || typ == sqltypes.VarChar {
return evalToVarchar(e, col, false), nil
} Type guard
func isCharTarget(typ sqltypes.Type) bool {
return typ == sqltypes.Char || typ == sqltypes.VarChar
} Try / catch
func safeCoerceToChar(e eval, col collations.ID) (out eval, err error) {
defer func() {
if r := recover(); r != nil {
err = vterrors.Errorf(vtrpcpb.Code_INTERNAL, "char coercion failed: %v", r)
}
}()
return evalToVarchar(e, col, false), nil
} Prevention
- Never rely on the fall-through switch for CHAR/VARCHAR; always use the explicit early branch
- Add a unit test coercing values to both Char and VarChar
- When refactoring evalCoerce, keep the early string branch and the switch's unreachable guard consistent
When it happens
Trigger: Refactoring evalCoerce so the early `typ == VarChar || typ == Char` return no longer catches all string coercions (or a new call path passes sqltypes.Char/sqltypes.VarChar expecting the switch to handle it).
Common situations: Engine development: modifying coercion logic in eval.go or adding a coercion call site that forgot the explicit VARCHAR path; not reachable by end users on stock Vitess.
Related errors
- EvalResult.coerce with no collation
- 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/eb97c31bc9ba9a07.
Report an issue: GitHub.