vitessio/vitess · error
can't convert %s to decimal: too many .s
Error message
can't convert %s to decimal: too many .s
What it means
Returned by decimal.Scan conversion helpers when the input string contains more than one '.' character, so it cannot be interpreted as a decimal literal.
Source
Thrown at go/mysql/decimal/scan.go:123
}
if len(s) <= 18 {
dec, err := parseDecimal64(s)
if err == nil {
if neg {
dec.value.Neg(dec.value)
}
return dec, nil
}
if err != errOverflow {
return Decimal{}, fmt.Errorf("can't convert %s to decimal: %v", original, err)
}
}
var fractional, integral []byte
if pIndex := bytes.IndexByte(s, '.'); pIndex >= 0 {
if bytes.IndexByte(s[pIndex+1:], '.') != -1 {
return Decimal{}, fmt.Errorf("can't convert %s to decimal: too many .s", original)
}
if pIndex+1 < len(s) {
integral = s[:pIndex]
fractional = s[pIndex+1:]
} else {
integral = s[:pIndex]
}
} else {
integral = s
}
// Check if the size of this bigint would fit in the limits
// that MySQL has by default. To do that, we must convert the
// length of our integral and fractional part to "mysql digits"
myintg := myBigDigits(int32(len(integral)))
myfrac := myBigDigits(int32(len(fractional)))
if myintg > MyMaxBigDigits {
return largestForm(MyMaxPrecision, 0, neg), nilView on GitHub (pinned to 01a25a7d17)
Solutions
- Inspect the quoted string in the error and fix the producer so only one dot is emitted.
- If joining integral/fractional parts programmatically, trim dots from the parts before concatenation.
- Validate the payload with a single-dot check before calling NewFromMySQL to fail earlier with better context.
Example fix
// before payload := integral + "." + fractional // fractional = "2.3" NewFromMySQL([]byte(payload)) // after fractional = strings.ReplaceAll(fractional, ".", "") payload := integral + "." + fractional
Defensive patterns
Strategy: validation
Validate before calling
func hasSingleDot(s []byte) bool {
return bytes.Count(s, []byte(".")) <= 1
}
// before calling:
// if !hasSingleDot(data) { return error } Try / catch
dec, err := decimal.NewFromMySQL(data)
if err != nil {
if strings.Contains(err.Error(), "too many .s") {
return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "malformed decimal %q", data)
}
return vterrors.Wrapf(err, "decimal conversion failed")
} Prevention
- When composing decimals from parts, ensure the parts contain no dots of their own.
- Validate payloads with a single-dot check before conversion.
- Avoid string concatenation for decimal construction; use the parser's native parts API when available.
- Log the offending string — it identifies the buggy producer immediately.
When it happens
Trigger: Calling NewFromMySQL with a payload like "1.2.3" — bytes.IndexByte finds the first dot, then a second dot in the remainder triggers the rejection.
Common situations: Concatenation bugs when building decimal strings (e.g. joining integral and fractional parts that already contain dots); corrupted wire data; callers passing formatted output like "1.23.45" from other systems.
Related errors
- overflow
- too many .s
- unexpected character %q
- can't convert %q to decimal: too short
- can't convert %s to decimal: %v
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/196b0f2599d3b6f1.
Report an issue: GitHub.