vitessio/vitess · error
unreachable
Error message
unreachable
What it means
Inside evalToDecimal, when converting an evalJSON value, the code switches on the JSON number kind. Any NumberType other than Int/Uint/Float hits the default branch and panics "unreachable". It encodes the assumption that JSON numbers only have those three representations.
Source
Thrown at go/vt/vtgate/evalengine/eval_numeric.go:282
switch e.NumberType() {
case json.NumberTypeSigned:
i, _ := e.Int64()
return newEvalDecimal(decimal.NewFromInt(i), m, d)
case json.NumberTypeUnsigned:
// If the value fits in an unsigned integer, convert to that
// and then cast it to a signed integer and then turn it into a decimal.
// SELECT CAST(CAST(18446744073709551615 AS JSON) AS DECIMAL) -> -1
u, _ := e.Uint64()
return newEvalDecimal(decimal.NewFromInt(int64(u)), m, d)
case json.NumberTypeDecimal:
dec, _ := e.Decimal()
return newEvalDecimal(dec, m, d)
case json.NumberTypeFloat:
f, _ := e.Float64()
dec := decimal.NewFromFloat(f)
return newEvalDecimal(dec, m, d)
default:
panic("unreachable")
}
case json.TypeString:
dec, _ := decimal.NewFromString(e.Raw())
return newEvalDecimal(dec, m, d)
default:
return newEvalDecimal(decimal.Zero, m, d)
}
case *evalTemporal:
return newEvalDecimal(e.toDecimal(), m, d)
case *evalEnum:
return newEvalDecimal(decimal.NewFromInt(enumNumeric(e.value)), m, d)
case *evalSet:
return newEvalDecimal(decimal.NewFromUint(e.set), m, d)
default:
panic("unsupported")
}
}
View on GitHub (pinned to 01a25a7d17)
Solutions
- Log/handle the unknown json.NumberType explicitly and treat it like NumberTypeFloat instead of panicking
- Check the json package version used; align eval_numeric.go's switch with the current NumberType enum
- If reachable via a query, work around by casting the JSON value to a numeric type explicitly in SQL before comparison
Example fix
// before
default:
panic("unreachable")
// after
default:
f, _ := e.Float64()
return newEvalDecimal(decimal.NewFromFloat(f), m, d) Defensive patterns
Strategy: validation
Validate before calling
nt, _ := jsonVal.Number()
if nt != json.NumberTypeInt && nt != json.NumberTypeUint && nt != json.NumberTypeFloat {
return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unsupported JSON number type %v", nt)
} Type guard
func isKnownJSONNumber(t json.NumberType) bool {
switch t {
case json.NumberTypeInt, json.NumberTypeUint, json.NumberTypeFloat:
return true
default:
return false
}
} Try / catch
defer func() {
if r := recover(); r != nil {
err = vterrors.Errorf(vtrpcpb.Code_INTERNAL, "evalToDecimal: %v", r)
}
}() Prevention
- After upgrading the json dependency, diff json.NumberType against the switch in eval_numeric.go
- Prefer explicit numeric casts in SQL for JSON values before decimal arithmetic
- Add a fuzz/unit test converting every NumberType through evalToDecimal
When it happens
Trigger: evalToDecimal receives an *evalJSON whose Number() returns a number type not in {Int, Uint, Float} — e.g. a new json.NumberType added by a library upgrade, or corrupted JSON state.
Common situations: Vitess/go-vtupgrades where the json library gained a new NumberType; passing malformed JSON documents through decimal comparison paths (compareAllDecimal, integerDivideConvert).
Related errors
- unreachable
- malformed hex literal from parser
- bad unsigned integer type
- bad type aggregation for signed/unsigned types
- unreachable
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/f01045a8b33a9dcd.
Report an issue: GitHub.