{"record":{"id":"b99cae9089b4eeba","repo":"vitessio/vitess","slug":"unhandled-case-evalistruthy","errorCode":null,"errorMessage":"unhandled case: evalIsTruthy","messagePattern":"unhandled case: evalIsTruthy","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtgate/evalengine/eval.go","lineNumber":171,"sourceCode":"\t\t\t}\n\t\t\treturn makeboolean(hex.u != 0)\n\t\t}\n\t\tif e.isBitLiteral() {\n\t\t\tbit, ok := e.toNumericBit()\n\t\t\tif !ok {\n\t\t\t\t// overflow\n\t\t\t\treturn makeboolean(true)\n\t\t\t}\n\t\t\treturn makeboolean(bit.i != 0)\n\t\t}\n\t\tf, _ := fastparse.ParseFloat64(e.string())\n\t\treturn makeboolean(f != 0.0)\n\tcase *evalJSON:\n\t\treturn makeboolean(e.ToBoolean())\n\tcase *evalTemporal:\n\t\treturn makeboolean(!e.isZero())\n\tdefault:\n\t\tpanic(\"unhandled case: evalIsTruthy\")\n\t}\n}\n\nfunc evalCoerce(e eval, typ sqltypes.Type, size, scale int32, col collations.ID, now time.Time, allowZero bool) (eval, error) {\n\tif e == nil {\n\t\treturn nil, nil\n\t}\n\tif col == collations.Unknown {\n\t\tpanic(\"EvalResult.coerce with no collation\")\n\t}\n\tif typ == sqltypes.VarChar || typ == sqltypes.Char {\n\t\t// if we have an explicit VARCHAR coercion, always force it so the collation is replaced in the target\n\t\treturn evalToVarchar(e, col, false)\n\t}\n\tif e.SQLType() == typ && e.Size() == size && e.Scale() == scale {\n\t\t// nothing to be done here\n\t\treturn e, nil\n\t}","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtgate/evalengine/eval.go#L153-L189","documentation":"evalIsTruthy converts an evaluated value to a boolean for truthiness checks (WHERE clauses, IF, AND/OR). The switch covers all eval types the engine can produce and panics on any other concrete eval implementation. Hitting it means an eval type exists that truthiness conversion was never taught to handle — an engine invariant violation.","triggerScenarios":"A new eval type (e.g. a hypothetical *evalTuple or a newly added temporal/geometry variant) reaches ToBoolean/eval without a case in evalIsTruthy's switch in eval.go; typically after adding a type to the engine without updating all switches.","commonSituations":"Developers extending the evalengine with new value types; end users see it as a vtgate crash from a specific expression (e.g. a WHERE or CASE using the unhandled type).","solutions":["Add a case for the missing eval type in evalIsTruthy (eval.go:~150) defining its truthiness semantics (mirror MySQL: nonzero numbers true, zero-date temporal false, etc.)","Search the codebase for other exhaustive switches over eval types (evalCoerce, evalToNumeric, evalConvert_nj) and update them in the same change","If it occurs on stock Vitess, capture the query and stack trace and file an issue"],"exampleFix":"// before\ncase *evalTemporal:\n\treturn makeboolean(!e.isZero())\ndefault:\n\tpanic(\"unhandled case: evalIsTruthy\")\n// after\ncase *evalTemporal:\n\treturn makeboolean(!e.isZero())\ncase *evalNewType:\n\treturn makeboolean(e.IsTruthy())\ndefault:\n\tpanic(\"unhandled case: evalIsTruthy\")","handlingStrategy":"type-guard","validationCode":"// Go: check the value type before truthiness evaluation\nswitch e.(type) {\ncase *evalInt8, *evalInt16, *evalInt32, *evalInt64, *evalUint64,\n\t*evalFloat, *evalDecimal, *evalBytes, *evalJSON, *evalTemporal:\n\t// safe to eval truthiness\ndefault:\n\treturn vterrors.Errorf(vtrpcpb.Code_INTERNAL, \"no truthiness for %T\", e)\n}","typeGuard":"func hasTruthySemantics(e eval) bool {\n\tswitch e.(type) {\n\tcase *evalInt8, *evalInt16, *evalInt32, *evalInt64,\n\t\t*evalUint64, *evalFloat, *evalDecimal, *evalBytes, *evalJSON, *evalTemporal:\n\t\treturn true\n\t}\n\treturn false\n}","tryCatchPattern":"func safeIsTruthy(e eval) (b boolean, err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = vterrors.Errorf(vtrpcpb.Code_INTERNAL, \"truthiness failed: %v\", r)\n\t\t}\n\t}()\n\treturn evalIsTruthy(e), nil\n}","preventionTips":["When adding an eval type, grep for `panic(\"unhandled case\"` and update each switch","Keep a central list of eval types and table-test every exhaustive switch against it","Validate operand types at plan time so unsupported types never reach runtime evaluation"],"tags":["go","panic","evalengine","type-system"],"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"}