vitessio/vitess · error
unreachable
Error message
unreachable
What it means
evalConvert_nj converts numeric eval values to JSON numbers. The switch maps each numeric eval type (int, uint, float, decimal) to a json value and the trailing panic('unreachable') asserts every numeric input had a case. It fires when a numeric value wrapped in a new/unhandled eval type (or a type misclassified as numeric) reaches the JSON conversion — an engine invariant violation.
Source
Thrown at go/vt/vtgate/evalengine/eval_json.go:100
return json.NewNumber(hack.String(f), json.NumberTypeFloat)
}
func evalConvert_nj(e evalNumeric) *evalJSON {
if e == evalBoolTrue {
return json.ValueTrue
}
if e == evalBoolFalse {
return json.ValueFalse
}
switch e := e.(type) {
case *evalInt64:
return json.NewNumber(hack.String(e.ToRawBytes()), json.NumberTypeSigned)
case *evalUint64:
return json.NewNumber(hack.String(e.ToRawBytes()), json.NumberTypeUnsigned)
case *evalDecimal:
return json.NewNumber(hack.String(e.ToRawBytes()), json.NumberTypeDecimal)
}
panic("unreachable")
}
func evalConvert_cj(e *evalBytes) (*evalJSON, error) {
jsonText, err := charset.Convert(nil, charset.Charset_utf8mb4{}, e.bytes, colldata.Lookup(e.col.Collation).Charset())
if err != nil {
return nil, err
}
var p json.Parser
return p.ParseBytes(jsonText)
}
func evalConvertArg_cj(e *evalBytes) (*evalJSON, error) {
jsonText, err := charset.Convert(nil, charset.Charset_utf8mb4{}, e.bytes, colldata.Lookup(e.col.Collation).Charset())
if err != nil {
return nil, err
}
return json.NewString(string(jsonText)), nil
}View on GitHub (pinned to 01a25a7d17)
Solutions
- Add the missing numeric eval type as a case in evalConvert_nj (eval_json.go:~85) producing the right json.Number
- Verify upstream type normalization (evalToNumeric/makeNumericAndPrioritize) so only known numeric evals reach this function
- Report the crashing CAST expression to Vitess with a stack trace if it occurs on stock code
Example fix
// before
case *evalDecimal:
return json.NewNumber(hack.String(e.ToRawBytes()), json.NumberTypeDecimal)
}
panic("unreachable")
// after
case *evalDecimal:
return json.NewNumber(hack.String(e.ToRawBytes()), json.NumberTypeDecimal)
case *evalNewNumeric:
return json.NewNumber(hack.String(e.ToRawBytes()), json.NumberTypeDecimal)
}
panic("unreachable") Defensive patterns
Strategy: type-guard
Validate before calling
// Go: confirm numeric kind before JSON conversion
switch e.(type) {
case *evalInt8, *evalInt16, *evalInt32, *evalInt64, *evalUint64, *evalFloat, *evalDecimal:
// safe for evalConvert_nj
default:
return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "non-numeric %T passed to JSON conversion", e)
} Type guard
func isNumericEval(e eval) bool {
switch e.(type) {
case *evalInt8, *evalInt16, *evalInt32, *evalInt64,
*evalUint64, *evalFloat, *evalDecimal:
return true
}
return false
} Try / catch
func safeConvertNJ(e eval) (j *evalJSON, err error) {
defer func() {
if r := recover(); r != nil {
err = vterrors.Errorf(vtrpcpb.Code_INTERNAL, "JSON conversion failed: %v", r)
}
}()
return evalConvert_nj(e.(*evalInt64)), nil // after type-guard
} Prevention
- Normalize through evalToNumeric before numeric-to-JSON conversion
- Update evalConvert_nj together with evalToNumeric/evalToFloat when adding numeric types
- CAST non-numeric expressions explicitly before assigning to JSON columns
When it happens
Trigger: A new eval numeric type added to the engine without updating evalConvert_nj, or evalToJSON/argToJSON routing a non-numeric eval into evalConvert_nj after makeNumericAndPrioritize failed to normalize it.
Common situations: Engine development: extending numeric types or JSON casting (CAST(x AS JSON)); users see it as a vtgate crash on queries casting the affected value to JSON.
Related errors
- unsupported
- unreachable
- 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/a992ed70aad56869.
Report an issue: GitHub.