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.VarCharView on GitHub (pinned to 01a25a7d17)
Solutions
- 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
- Add a unit test in the evalengine package reproducing the type combination to pin the aggregation result
- 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
- Unit-test typeAggregation with mixed signed/unsigned combinations covering every integer width
- Assert ta.unsignedMax is set whenever ta.unsigned > 0 in aggregate()
- Keep MySQL's aggregation table (docs in result()) as the test oracle
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
- bad unsigned integer type
- unreachable
- malformed hex literal from parser
- negative stack position
- unhandled case: evalIsTruthy
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/27ca695522f99ead.
Report an issue: GitHub.