vitessio/vitess · error
can't convert %s to decimal: %v
Error message
can't convert %s to decimal: %v
What it means
NewFromMySQL wraps any error from parseDecimal64 that is not errOverflow with this generic message, embedding the original string and the underlying cause (e.g. "unexpected character"). It is the catch-all conversion failure for long (>18 char) decimal strings or short strings whose parse failed for a non-overflow reason.
Source
Thrown at go/mysql/decimal/scan.go:116
neg = true
s = s[1:]
}
}
if len(s) == 0 {
return Decimal{}, fmt.Errorf("can't convert %q to decimal: too short", original)
}
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
}
View on GitHub (pinned to 01a25a7d17)
Solutions
- Read the wrapped %v cause in the message to identify the exact problem character or condition.
- Sanitize the input so it contains only optional sign, digits, and at most one dot.
- Confirm the sender encodes DECIMAL in MySQL binary format before transmission.
- If the value is user text, parse it with a lenient formatter first, then feed clean digits to NewFromMySQL.
Example fix
// before
NewFromMySQL([]byte("12,345.67")) // comma -> error
// after
s := strings.ReplaceAll("12,345.67", ",", "")
NewFromMySQL([]byte(s)) Defensive patterns
Strategy: validation
Validate before calling
func isPlainDecimal(s string) bool {
s = strings.TrimLeft(s, "+-")
parts := strings.SplitN(s, ".", 2)
for _, p := range parts {
if p == "" {
continue
}
for _, c := range p {
if c < '0' || c > '9' {
return false
}
}
}
return s != ""
} Try / catch
dec, err := decimal.NewFromMySQL(data)
if err != nil {
// the wrapped cause names the bad character; log payload + cause
return vterrors.Wrapf(err, "decimal conversion failed for %q", data)
} Prevention
- Inspect the wrapped %v cause — it pinpoints the offending character.
- Normalize user-formatted numbers (remove separators) before conversion.
- Keep a single serialization path for DECIMAL data end to end.
- Test with payloads from every producer in your pipeline.
When it happens
Trigger: NewFromMySQL receives a decimal string that fails parseDecimal64 with a syntax error (invalid character) rather than overflow — typically because the string contains a character other than digits/dot/sign.
Common situations: Non-numeric bytes inside a DECIMAL wire payload; double-encoded text where the payload is a string representation with unexpected characters; corrupted replication or proxy traffic.
Related errors
- overflow
- unexpected character %q
- can't convert %q to decimal: too short
- can't convert %s to decimal: too many .s
- too many .s
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/4f72fd3240d7dedb.
Report an issue: GitHub.