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

  1. Add the missing numeric eval type as a case in evalConvert_nj (eval_json.go:~85) producing the right json.Number
  2. Verify upstream type normalization (evalToNumeric/makeNumericAndPrioritize) so only known numeric evals reach this function
  3. 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

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


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