{"record":{"id":"e426e56631731eb7","repo":"vitessio/vitess","slug":"value-foreachvalue-on-non-tuple","errorCode":null,"errorMessage":"Value.ForEachValue on non-tuple","messagePattern":"Value\\.ForEachValue on non-tuple","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/sqltypes/value.go","lineNumber":791,"sourceCode":"\nfunc encodeTuple(tuple []Value) []byte {\n\tvar total int\n\tfor _, v := range tuple {\n\t\ttotal += len(v.val) + 3\n\t}\n\n\tbuf := make([]byte, 0, total)\n\tfor _, v := range tuple {\n\t\tbuf = protowire.AppendVarint(buf, uint64(v.typ))\n\t\tbuf = protowire.AppendVarint(buf, uint64(len(v.val)))\n\t\tbuf = append(buf, v.val...)\n\t}\n\treturn buf\n}\n\nfunc (v *Value) ForEachValue(each func(bv Value)) error {\n\tif v.Type() != Tuple {\n\t\tpanic(\"Value.ForEachValue on non-tuple\")\n\t}\n\n\tvar sz, ty uint64\n\tvar varlen int\n\tbuf := v.val\n\tfor len(buf) > 0 {\n\t\tty, varlen = protowire.ConsumeVarint(buf)\n\t\tif varlen < 0 {\n\t\t\treturn ErrBadTupleEncoding\n\t\t}\n\n\t\tbuf = buf[varlen:]\n\t\tsz, varlen = protowire.ConsumeVarint(buf)\n\t\tif varlen < 0 {\n\t\t\treturn ErrBadTupleEncoding\n\t\t}\n\n\t\tbuf = buf[varlen:]","sourceCodeStart":773,"sourceCodeEnd":809,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/sqltypes/value.go#L773-L809","documentation":"Value.ForEachValue iterates the packed elements of a Tuple-typed Value. Calling it on any other type panics, because non-tuple values have no length-prefixed element encoding to walk. Callers must ensure the value is a tuple before iterating.","triggerScenarios":"Calling v.ForEachValue(fn) on a Value whose type is not sqltypes.Tuple — e.g. a VarBinary or Int64 value fetched from a result set that is assumed (but not verified) to be a tuple.","commonSituations":"Processing multi-column results where a column was expected to be a tuple but schema/type inference changed; passing query results directly to ForEachValue without checking Type(); version changes in result encoding.","solutions":["Check v.Type() == sqltypes.Tuple (or handle the returned error path) before calling ForEachValue","Return an error to the caller instead of assuming tuple-ness when unpacking bind variable results","Fix the upstream code that produced a non-tuple Value where a tuple was expected"],"exampleFix":"// before\nv.ForEachValue(func(bv sqltypes.Value) { ... }) // panics on non-tuple\n// after\nif v.Type() != sqltypes.Tuple {\n    return fmt.Errorf(\"expected tuple, got %v\", v.Type())\n}\nv.ForEachValue(func(bv sqltypes.Value) { ... })","handlingStrategy":"type-guard","validationCode":"if v.Type() != sqltypes.Tuple {\n    return errors.New(\"ForEachValue requires a tuple\")\n}","typeGuard":"func isTuple(v sqltypes.Value) bool { return v.Type() == sqltypes.Tuple }","tryCatchPattern":null,"preventionTips":["Always check Type() before unpacking a Value","Verify the upstream producer actually emits tuples (e.g. multi-column bind results)","Centralize tuple unpacking in one guarded helper"],"tags":["go","panic","sqltypes","tuple"],"backgroundTag":"unexpected-value-type","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}