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

  1. Identify and patch/upgrade the server or proxy returning the bad column metadata (capture via SHOW WARNINGS / EXPLAIN / general log).
  2. For that column, scan raw bytes instead of relying on driver formatting (read into []byte and parse yourself).
  3. Re-run the query without prepared statements to use the text protocol, which bypasses formatBinaryDateTime entirely.
  4. 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

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


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)