{"record":{"id":"a992ed70aad56869","repo":"vitessio/vitess","slug":"unreachable-a992ed","errorCode":null,"errorMessage":"unreachable","messagePattern":"unreachable","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtgate/evalengine/eval_json.go","lineNumber":100,"sourceCode":"\treturn json.NewNumber(hack.String(f), json.NumberTypeFloat)\n}\n\nfunc evalConvert_nj(e evalNumeric) *evalJSON {\n\tif e == evalBoolTrue {\n\t\treturn json.ValueTrue\n\t}\n\tif e == evalBoolFalse {\n\t\treturn json.ValueFalse\n\t}\n\tswitch e := e.(type) {\n\tcase *evalInt64:\n\t\treturn json.NewNumber(hack.String(e.ToRawBytes()), json.NumberTypeSigned)\n\tcase *evalUint64:\n\t\treturn json.NewNumber(hack.String(e.ToRawBytes()), json.NumberTypeUnsigned)\n\tcase *evalDecimal:\n\t\treturn json.NewNumber(hack.String(e.ToRawBytes()), json.NumberTypeDecimal)\n\t}\n\tpanic(\"unreachable\")\n}\n\nfunc evalConvert_cj(e *evalBytes) (*evalJSON, error) {\n\tjsonText, err := charset.Convert(nil, charset.Charset_utf8mb4{}, e.bytes, colldata.Lookup(e.col.Collation).Charset())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar p json.Parser\n\treturn p.ParseBytes(jsonText)\n}\n\nfunc evalConvertArg_cj(e *evalBytes) (*evalJSON, error) {\n\tjsonText, err := charset.Convert(nil, charset.Charset_utf8mb4{}, e.bytes, colldata.Lookup(e.col.Collation).Charset())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn json.NewString(string(jsonText)), nil\n}","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtgate/evalengine/eval_json.go#L82-L118","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\ncase *evalDecimal:\n\treturn json.NewNumber(hack.String(e.ToRawBytes()), json.NumberTypeDecimal)\n}\npanic(\"unreachable\")\n// after\ncase *evalDecimal:\n\treturn json.NewNumber(hack.String(e.ToRawBytes()), json.NumberTypeDecimal)\ncase *evalNewNumeric:\n\treturn json.NewNumber(hack.String(e.ToRawBytes()), json.NumberTypeDecimal)\n}\npanic(\"unreachable\")","handlingStrategy":"type-guard","validationCode":"// Go: confirm numeric kind before JSON conversion\nswitch e.(type) {\ncase *evalInt8, *evalInt16, *evalInt32, *evalInt64, *evalUint64, *evalFloat, *evalDecimal:\n\t// safe for evalConvert_nj\ndefault:\n\treturn vterrors.Errorf(vtrpcpb.Code_INTERNAL, \"non-numeric %T passed to JSON conversion\", e)\n}","typeGuard":"func isNumericEval(e eval) bool {\n\tswitch e.(type) {\n\tcase *evalInt8, *evalInt16, *evalInt32, *evalInt64,\n\t\t*evalUint64, *evalFloat, *evalDecimal:\n\t\treturn true\n\t}\n\treturn false\n}","tryCatchPattern":"func safeConvertNJ(e eval) (j *evalJSON, err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = vterrors.Errorf(vtrpcpb.Code_INTERNAL, \"JSON conversion failed: %v\", r)\n\t\t}\n\t}()\n\treturn evalConvert_nj(e.(*evalInt64)), nil // after type-guard\n}","preventionTips":["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"],"tags":["go","panic","evalengine","json","type-conversion"],"backgroundTag":"unhandled-type-switch-panic","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}