vitessio/vitess · error
Value.ForEachValue on non-tuple
Error message
Value.ForEachValue on non-tuple
What it means
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.
Source
Thrown at go/sqltypes/value.go:791
func encodeTuple(tuple []Value) []byte {
var total int
for _, v := range tuple {
total += len(v.val) + 3
}
buf := make([]byte, 0, total)
for _, v := range tuple {
buf = protowire.AppendVarint(buf, uint64(v.typ))
buf = protowire.AppendVarint(buf, uint64(len(v.val)))
buf = append(buf, v.val...)
}
return buf
}
func (v *Value) ForEachValue(each func(bv Value)) error {
if v.Type() != Tuple {
panic("Value.ForEachValue on non-tuple")
}
var sz, ty uint64
var varlen int
buf := v.val
for len(buf) > 0 {
ty, varlen = protowire.ConsumeVarint(buf)
if varlen < 0 {
return ErrBadTupleEncoding
}
buf = buf[varlen:]
sz, varlen = protowire.ConsumeVarint(buf)
if varlen < 0 {
return ErrBadTupleEncoding
}
buf = buf[varlen:]View on GitHub (pinned to 01a25a7d17)
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
Example fix
// before
v.ForEachValue(func(bv sqltypes.Value) { ... }) // panics on non-tuple
// after
if v.Type() != sqltypes.Tuple {
return fmt.Errorf("expected tuple, got %v", v.Type())
}
v.ForEachValue(func(bv sqltypes.Value) { ... }) Defensive patterns
Strategy: type-guard
Validate before calling
if v.Type() != sqltypes.Tuple {
return errors.New("ForEachValue requires a tuple")
} Type guard
func isTuple(v sqltypes.Value) bool { return v.Type() == sqltypes.Tuple } Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- panic(err)
- panic: %v
- SetIdleTimeout called when timer not initialized
- negative capacity
- timestampBusy when borrowing a time
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/e426e56631731eb7.
Report an issue: GitHub.