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

  1. Check v.Type() == sqltypes.Tuple (or handle the returned error path) before calling ForEachValue
  2. Return an error to the caller instead of assuming tuple-ness when unpacking bind variable results
  3. 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

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


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