go-sql-driver/mysql · critical

protocol error, illegal decimals value

Error message

protocol error, illegal decimals value %d

What it means

While decoding binary-protocol result rows, the column metadata's 'decimals' byte for TIME columns must be 0, 0x1f, or 1-6. A value outside that range (e.g. 7-30, or 31 other than 0x1f) means the packet is corrupt or non-conformant, so the driver aborts with this protocol error.

Solutions

  1. Retry the query once (transient corruption often does not recur).
  2. If reproducible, verify the server/proxy is MySQL-protocol-conformant and update or replace it.
  3. Check network/TLS/MTU health and packet integrity; capture a packet trace if it persists.
  4. Report upstream with the decimals value if it reproduces against a compliant server.

Example fix

null
Defensive patterns

Strategy: validation

Validate before calling

// No caller-side validation possible; this is a wire-integrity check. Best mitigation:
// use TLS and keep the server/proxy on supported, conformant versions.

Type guard

null

Try / catch

// Treat as transient/protocol failure; retry with backoff, then surface.
if err := rows.Scan(...); err != nil && strings.Contains(err.Error(), "illegal decimals") {
    // retry once; if it persists, investigate server/proxy/network
}

Prevention

When it happens

Trigger: A malformed/corrupted packet from the server or an intermediary; a buggy or non-MySQL-compatible proxy mangling the column metadata; transient memory/network corruption. Extremely rare with compliant servers.

Common situations: Flaky network, a non-conforming proxy or MySQL fork, or hardware corruption on the wire; intermittent and hard to reproduce.

Related errors


AI-assisted analysis of go-sql-driver/mysql@03d76c7e07 (2026-08-07). Data as JSON: /api/errors/86250a91a80494fa. Report an issue: GitHub.

Appendix: 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 03d76c7e07)