{"record":{"id":"3332c83664a99a4a","repo":"vitessio/vitess","slug":"did-not-typecheck-cardinality","errorCode":null,"errorMessage":"did not typecheck cardinality","messagePattern":"did not typecheck cardinality","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtgate/evalengine/expr_compare.go","lineNumber":288,"sourceCode":"\t\treturn compareNumeric(lf, rf)\n\t}\n}\n\n// fallbackBinary compares two values of the same type using the fallback binary comparison.\n// This is for types we don't yet properly support otherwise but do end up being used\n// for comparisons, for example when using vdiff.\n// TODO: Clean this up as we add more properly supported types and comparisons.\nfunc fallbackBinary(t sqltypes.Type) bool {\n\tswitch t {\n\tcase sqltypes.Bit, sqltypes.Enum, sqltypes.Set, sqltypes.Geometry, sqltypes.Vector:\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc evalCompareTuplesNullSafe(left, right []eval, collationEnv *collations.Environment) (int, error) {\n\tif len(left) != len(right) {\n\t\tpanic(\"did not typecheck cardinality\")\n\t}\n\tfor idx, lResult := range left {\n\t\tres, err := evalCompareNullSafe(lResult, right[idx], collationEnv)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tif res != 0 {\n\t\t\treturn res, nil\n\t\t}\n\t}\n\treturn 0, nil\n}\n\n// eval implements the expression interface\nfunc (c *ComparisonExpr) eval(env *ExpressionEnv) (eval, error) {\n\tleft, err := c.Left.eval(env)\n\tif err != nil {\n\t\treturn nil, err","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtgate/evalengine/expr_compare.go#L270-L306","documentation":"evalCompareTuplesNullSafe compares two row-value tuples element-wise and assumes the typechecker already guaranteed both tuples have the same cardinality. The panic fires when left and right tuple expressions have different lengths, meaning the earlier compile-time cardinality check was bypassed or buggy. It protects the index-based loop from out-of-range access.","triggerScenarios":"Evaluating a null-safe tuple comparison (e.g., `(a,b) <=> (c,d)` or IN over row constructors) where the two tuple sides evaluated to different numbers of elements — possible only if compile-time validation of tuple lengths was skipped or produced mismatched expression trees.","commonSituations":"Queries with row constructors of unequal arity that reached evaluation due to a planbuilder bug; custom code constructing TupleExpr comparisons without arity validation; evalengine development/tests.","solutions":["Verify the query's row constructors have equal element counts on both sides","Check the comparison expression's compile path ran its cardinality typecheck; fix the missing validation if bypassed","In custom callers, assert len(left)==len(right) before invoking tuple comparison","File a Vitess bug with the query if produced by normal SQL"],"exampleFix":"// before\nres, err := evalCompareTuplesNullSafe(l, r, env.CollationEnv())\n// after\nif len(l) != len(r) {\n\treturn nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, \"tuple cardinality mismatch: %d vs %d\", len(l), len(r))\n}\nres, err := evalCompareTuplesNullSafe(l, r, env.CollationEnv())","handlingStrategy":"validation","validationCode":"// Before issuing row-constructor comparisons, ensure equal arity on both sides\nfunc tupleArityMatch(nLeft, nRight int) error {\n\tif nLeft != nRight {\n\t\treturn fmt.Errorf(\"row constructor arity mismatch: %d vs %d\", nLeft, nRight)\n\t}\n\treturn nil\n}","typeGuard":"func sameTupleCardinality(l, r evalengine.TupleExpr) bool {\n\treturn len(l.Values) == len(r.Values)\n}","tryCatchPattern":"func safeCompare(l, r []evalengine.eval, env *collations.Environment) (res int, err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = fmt.Errorf(\"tuple compare panic: %v\", r)\n\t\t}\n\t}()\n\treturn evalengine.EvalCompareTuplesNullSafe(l, r, env)\n}","preventionTips":["Write row constructors with matching element counts on both sides","Keep planbuilder's tuple cardinality typecheck intact and covered by tests","Avoid hand-building tuple comparisons without arity validation","Add compile-time assertions in tests for IN/row-constructor planning"],"tags":["evalengine","panic","tuple-comparison","typecheck"],"backgroundTag":"tuple-cardinality-mismatch","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}