vitessio/vitess · error

unsupported type %T

Error message

unsupported type %T

What it means

evalToFloat converts any eval engine value to a float. When handed a value whose concrete type is not one of the handled numeric/enum/set/string-ish types, it panics with "unsupported type %T". This indicates the eval engine received an AST/expr value type that numeric coercion was never designed to handle, i.e. an internal invariant violation rather than user data error.

Source

Thrown at go/vt/vtgate/evalengine/eval_numeric.go:229

			}
			return &evalFloat{f: 0.0}, true
		case json.TypeNumber:
			f, ok := e.Float64()
			return &evalFloat{f: f}, ok
		case json.TypeString:
			val, err := fastparse.ParseFloat64(e.Raw())
			return &evalFloat{f: val}, err == nil
		default:
			return &evalFloat{f: 0}, true
		}
	case *evalTemporal:
		return &evalFloat{f: e.toFloat()}, true
	case *evalEnum:
		return &evalFloat{f: float64(enumNumeric(e.value))}, e.value != -1
	case *evalSet:
		return &evalFloat{f: float64(e.set)}, true
	default:
		panic(fmt.Sprintf("unsupported type %T", e))
	}
}

func evalToDecimal(e eval, m, d int32) *evalDecimal {
	switch e := e.(type) {
	case evalNumeric:
		return e.toDecimal(m, d)
	case *evalBytes:
		if e.isHexLiteral() {
			hex, ok := e.toNumericHex()
			if !ok {
				// overflow
				return newEvalDecimal(decimal.Zero, m, d)
			}
			return hex.toDecimal(m, d)
		}
		if e.isBitLiteral() {
			bit, ok := e.toNumericBit()

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Identify the type printed in the panic (%T) and add a case for it in evalToFloat in go/vt/vtgate/evalengine/eval_numeric.go
  2. If the type should never reach float coercion, fix the caller (evalCoerce/evalCompare) to route or reject that type earlier
  3. Check the vitess version; upgrade to a release where the new type is supported in numeric coercion

Example fix

// before
default:
    panic(fmt.Sprintf("unsupported type %T", e))
// after
case *evalMyNewType:
    return &evalFloat{f: e.toFloat()}, true
default:
    panic(fmt.Sprintf("unsupported type %T", e))
Defensive patterns

Strategy: type-guard

Validate before calling

// before relying on float coercion
if _, ok := v.(evalNumeric); !ok {
    switch v.(type) {
    case *evalEnum, *evalSet:
    default:
        return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "cannot coerce %T to float", v)
    }
}

Type guard

func isFloatCoercible(e eval) bool {
    switch e.(type) {
    case evalNumeric, *evalEnum, *evalSet:
        return true
    default:
        return false
    }
}

Try / catch

// Go panics are not errors; recover at query-evaluation boundary
func safeEvalToFloat(e eval) (f *evalFloat, err error) {
    defer func() {
        if r := recover(); r != nil {
            err = vterrors.Errorf(vtrpcpb.Code_INTERNAL, "evalToFloat: %v", r)
        }
    }()
    fl, _ := evalToFloat(e)
    return fl, nil
}

Prevention

When it happens

Trigger: Calling evalToFloat (directly or via evalCoerce, valueToEvalCast, evalCompare, compareAllFloat) with an eval implementation not covered by the type switch, e.g. a newly added eval type (like a new temporal or JSON variant) that was not added to the switch in eval_numeric.go.

Common situations: Developers extending the eval engine with a new eval type but forgetting to update evalToFloat; plugin or comparison code feeding an unexpected expression result into float comparison.

Related errors


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