go-sql-driver/mysql · error
protocol error, illegal decimals value %d
Error message
protocol error, illegal decimals value %d
What it means
A protocol-level error while decoding a binary-protocol TIME column from a prepared-statement result row (packets.go:1409): the column's decimals metadata byte is not 0x00, 0x1f, or 1-6, the only legal values per the MySQL binary protocol. The driver's adjacent comment urges reporting it, signaling it should not occur with a conformant server. It indicates a malformed or non-conforming server/proxy response.
Source
Thrown at packets.go:1409
fieldTypeTimestamp, fieldTypeDateTime: // Timestamp YYYY-MM-DD HH:MM:SS[.fractal]
num, isNull, n := readLengthEncodedInteger(data[pos:])
pos += n
switch {
case isNull:
dest[i] = nil
continue
case rows.rs.columns[i].fieldType == fieldTypeTime:
// database/sql does not support an equivalent to TIME, return a string
var dstlen uint8
switch decimals := rows.rs.columns[i].decimals; decimals {
case 0x00, 0x1f:
dstlen = 8
case 1, 2, 3, 4, 5, 6:
dstlen = 8 + 1 + decimals
default:
return fmt.Errorf(
"protocol error, illegal decimals value %d",
rows.rs.columns[i].decimals,
)
}
dest[i], err = formatBinaryTime(data[pos:pos+int(num)], dstlen)
case rows.mc.parseTime:
dest[i], err = parseBinaryDateTime(num, data[pos:], rows.mc.cfg.Loc)
default:
var dstlen uint8
if rows.rs.columns[i].fieldType == fieldTypeDate {
dstlen = 10
} else {
switch decimals := rows.rs.columns[i].decimals; decimals {
case 0x00, 0x1f:
dstlen = 19
case 1, 2, 3, 4, 5, 6:
dstlen = 19 + 1 + decimals
default:View on GitHub (pinned to c426bd9379)
Solutions
- Reproduce against a vanilla MySQL/MariaDB server to confirm it is a server/proxy bug.
- Upgrade or patch the offending server or proxy that generates the result set.
- Work around it by casting the column in SQL, e.g. SELECT CAST(t AS CHAR) AS t.
- Report the bug to the driver maintainers with the server/proxy version details.
Example fix
// before
rows, _ := db.Query("SELECT t FROM events") // t is TIME with malformed metadata
// after — force a string to bypass binary TIME decoding
rows, _ := db.Query("SELECT CAST(t AS CHAR) AS t FROM events") Defensive patterns
Strategy: fallback
Try / catch
if err := rows.Scan(dest...); err != nil {
if strings.Contains(err.Error(), "illegal decimals value") && strings.Contains(err.Error(), "protocol error") {
// server/proxy sent malformed TIME metadata; re-issue with CAST(t AS CHAR)
}
} Prevention
- Test new server/proxy versions against TIME columns before rollout.
- Use CAST(t AS CHAR) for TIME columns if a middleware mangles metadata.
- Report protocol errors to the driver and server maintainers.
When it happens
Trigger: Reading rows from a prepared statement over a connection to a buggy/non-conforming server or middleware (ProxySQL, Vitess, a custom sharding layer) that emits an invalid decimals byte for a TIME column; packet corruption in transit.
Common situations: A proxy generating malformed column definitions; an extremely old or patched server; rare genuine driver/server version skew.
Related errors
- unknown field type %d
- illegal TIME length %d
- invalid TIME packet length %d
- invalid timeTruncate value: %v, error: %w
- MySQL server does not support required protocol 41+
AI-assisted analysis of go-sql-driver/mysql@c426bd9379 (2026-08-04).
Data as JSON: /data/errors/86250a91a80494fa.json.
Report an issue: GitHub.