go-sql-driver/mysql · error
illegal length
Error message
illegal %s length %d
What it means
Raised by formatBinaryDateTime (utils.go:404) for the DISPLAY length. After receiving a binary DATE/DATETIME, the driver computes a target display length from the column's decimals metadata; if that length is not in {10, 19, 21-26} the driver refuses to format it (type becomes 'DATE' or 'DATETIME'). Because the preceding decimals switch in packets.go already rejects invalid decimals, reaching this line means the column metadata itself reported an inconsistent length/decimals combination — effectively a protocol-level integrity violation.
Solutions
- Identify and patch/upgrade the server or proxy returning the bad column metadata (capture via SHOW WARNINGS / EXPLAIN / general log).
- For that column, scan raw bytes instead of relying on driver formatting (read into []byte and parse yourself).
- Re-run the query without prepared statements to use the text protocol, which bypasses formatBinaryDateTime entirely.
- Reconnect/re-prepare to refresh cached metadata if the schema was altered.
Example fix
// before: relies on driver binary->text formatting db.QueryRow(`SELECT ts FROM t WHERE id=?`, id).Scan(&s) // after: read raw and parse, or switch to text protocol var raw []byte db.QueryRow(`SELECT CAST(ts AS CHAR) FROM t WHERE id=?`, id).Scan(&raw)
Defensive patterns
Strategy: try-catch
Validate before calling
// Column metadata is server-controlled; validate by reading the column's
// length/decimals via INFORMATION_SCHEMA before relying on binary formatting.
func validDateDisplayLen(l uint8) bool {
switch l {
case 10, 19, 21, 22, 23, 24, 25, 26:
return true
}
return false
} Type guard
func isCanonicalDateDisplayLen(l uint8) bool {
switch l {
case 10, 19, 21, 22, 23, 24, 25, 26:
return true
}
return false
} Try / catch
var s string
if err := db.QueryRow(q, id).Scan(&s); err != nil {
if strings.Contains(err.Error(), "illegal") && strings.Contains(err.Error(), "length") {
var raw []byte
_ = db.QueryRow(`SELECT CAST(col AS CHAR) FROM t WHERE id=?`, id).Scan(&raw)
s = string(raw)
}
} Prevention
- Use CAST(... AS CHAR) for temporal columns served by proxies of uncertain compliance.
- Avoid prepared statements for DATETIME columns through flaky middleware.
- Re-prepare statements after schema changes to refresh metadata.
When it happens
Trigger: Reading a DATE/DATETIME/TIMESTAMP column from a prepared-statement result set (parseTime=false, so the driver formats the binary value to text) where the column metadata advertises a length outside the canonical set. Reached via formatBinaryDateTime at packets.go:1434. Realistically only with a non-conforming server/proxy or after packet desync, since the driver pre-validates decimals.
Common situations: Buggy MySQL-compatible server or middleware returning malformed column definitions, result-set schema drift after an ALTER that the proxy cached incorrectly, packet corruption, or a server speaking a non-standard temporal encoding.
Related errors
- illegal packet length
- invalid DATETIME packet length
- illegal TIME length
- invalid TIME packet length
- bad value for field: `%c`
AI-assisted analysis of go-sql-driver/mysql@03d76c7e07 (2026-08-07).
Data as JSON: /api/errors/17ec3353208d0ef6.
Report an issue: GitHub.
Appendix: source
Thrown at utils.go:404
}
func formatBinaryDateTime(src []byte, length uint8) (driver.Value, error) {
// length expects the deterministic length of the zero value,
// negative time and 100+ hours are automatically added if needed
if len(src) == 0 {
return zeroDateTime[:length], nil
}
var dst []byte // return value
var p1, p2, p3 byte // current digit pair
switch length {
case 10, 19, 21, 22, 23, 24, 25, 26:
default:
t := "DATE"
if length > 10 {
t += "TIME"
}
return nil, fmt.Errorf("illegal %s length %d", t, length)
}
switch len(src) {
case 4, 7, 11:
default:
t := "DATE"
if length > 10 {
t += "TIME"
}
return nil, fmt.Errorf("illegal %s packet length %d", t, len(src))
}
dst = make([]byte, 0, length)
// start with the date
year := binary.LittleEndian.Uint16(src[:2])
pt := year / 100
p1 = byte(year - 100*uint16(pt))
p2, p3 = src[2], src[3]
dst = append(dst,
digits10[pt], digits01[pt],View on GitHub (pinned to 03d76c7e07)