vitessio/vitess · error

bad type aggregation for signed/unsigned types

Error message

bad type aggregation for signed/unsigned types

What it means

typeAggregation.result computes the aggregated type when all operands are numeric. After handling all-signed and all-unsigned cases, if ta.signed == 0 the state is inconsistent (a numeric mix with zero signed operands already fell into the all-unsigned branch), so it panics. This is a defensive invariant guard mirroring MySQL's mixed signed/unsigned aggregation rules and should be unreachable for well-formed aggregation state.

Source

Thrown at go/vt/vtgate/evalengine/api_type_aggregation.go:255

			ta.signedMax = sqltypes.Int32
		}
	}

	if ta.double+ta.decimal+ta.signed+ta.unsigned == ta.total {
		if ta.double > 0 {
			return sqltypes.Float64
		}
		if ta.decimal > 0 {
			return sqltypes.Decimal
		}
		if ta.signed == ta.total {
			return ta.signedMax
		}
		if ta.unsigned == ta.total {
			return ta.unsignedMax
		}
		if ta.signed == 0 {
			panic("bad type aggregation for signed/unsigned types")
		}
		agtype := nextSignedTypeForUnsigned(ta.unsignedMax)
		if sqltypes.IsSigned(agtype) {
			return max(agtype, ta.signedMax)
		}
		return agtype
	}

	if ta.char == ta.total {
		return sqltypes.VarChar
	}
	if ta.char+ta.binary == ta.total {
		// HACK: this is not in the official documentation, but groups of strings where
		// one of the strings is not directly a VARCHAR or VARBINARY (e.g. a hex literal,
		// or a VARCHAR that has been explicitly collated) will result in VARCHAR when
		// aggregated
		if ta.charother > 0 {
			return sqltypes.VarChar

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Audit how ta.unsigned/ta.signed/ta.unsignedMax are updated in aggregate() (api_type_aggregation.go) for the types in your query and fix the miscount
  2. Add a unit test in the evalengine package reproducing the type combination to pin the aggregation result
  3. Report the query and schema to the Vitess project with the panic stack trace if it occurs on unmodified code
Defensive patterns

Strategy: validation

Validate before calling

// Go: sanity-check aggregation state before result()
if ta.double+ta.decimal+ta.signed+ta.unsigned == ta.total &&
	ta.signed == 0 && ta.unsigned != ta.total {
	// inconsistent state, do not call result()
	return sqltypes.VarChar, nil
}

Type guard

func aggregationStateValid(ta *typeAggregation) bool {
	return ta.unsigned == 0 || ta.unsignedMax != 0
}

Try / catch

func (ta *typeAggregation) safeResult() (t sqltypes.Type, err error) {
	defer func() {
		if r := recover(); r != nil {
			err = vterrors.Errorf(vtrpcpb.Code_INTERNAL, "bad aggregation: %v", r)
		}
	}()
	return ta.result(), nil
}

Prevention

When it happens

Trigger: Mixed numeric aggregation (e.g. `unsigned_col = signed_col`, arithmetic in SELECT lists) where the aggregation counters got corrupted — e.g. a type counted as unsigned in ta.unsigned but its max type ta.unsignedMax was not set to an unsigned type, or new type-introduction code double-counts/mis-counts operands.

Common situations: Developers extending the evalengine with new types or changing type classification; not reachable by end users through normal SQL on supported Vitess types.

Related errors


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