go-sql-driver/mysql · error

illegal TIME length

Error message

illegal TIME length %d

What it means

Raised by formatBinaryTime (utils.go:460) for the DISPLAY length. The driver computes a target display length for a binary TIME value from the column's decimals; valid lengths are {8 (no fractional), 10-15 (with 1-6 fractional digits)}. An out-of-range length means the column metadata is internally inconsistent. Because packets.go pre-validates decimals, reaching this line implies protocol corruption rather than a normal data value.

Solutions

  1. Patch/upgrade the server or proxy returning invalid TIME metadata; verify with a direct mysql client.
  2. Read the TIME column raw ([]byte) or CAST(... AS CHAR) in SQL to bypass binary formatting.
  3. Run the query without prepared statements to use the text protocol.
  4. Re-prepare / reconnect to refresh column metadata.

Example fix

// before: driver formats binary TIME
db.QueryRow(`SELECT duration FROM t WHERE id=?`, id).Scan(&dur)

// after: let the server convert to text
var dur string
db.QueryRow(`SELECT CAST(duration AS CHAR) FROM t WHERE id=?`, id).Scan(&dur)
Defensive patterns

Strategy: try-catch

Validate before calling

func validTimeDisplayLen(l uint8) bool {
    switch l {
    case 8, 10, 11, 12, 13, 14, 15:
        return true
    }
    return false
}

Type guard

func isCanonicalTimeDisplayLen(l uint8) bool {
    switch l {
    case 8, 10, 11, 12, 13, 14, 15:
        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 TIME length") {
        var raw []byte
        _ = db.QueryRow(`SELECT CAST(dur AS CHAR) FROM t WHERE id=?`, id).Scan(&raw)
        s = string(raw)
    }
}

Prevention

When it happens

Trigger: Reading a TIME column from a prepared-statement result set (parseTime=false) when the column metadata yields a display length outside {8, 10-15}. Reached via formatBinaryTime at packets.go:1414. Practically limited to non-conforming MySQL-compatible servers, stale cached metadata after schema changes, or packet desync.

Common situations: A proxy/sharder returning malformed TIME column definitions, schema drift after ALTER TIME column, or an exotic server encoding TIME differently than the standard binary protocol.

Related errors


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

Appendix: source

Thrown at utils.go:460

		digits10[p3], digits01[p3],
	)
	return appendMicrosecs(dst, src[2:], int(length)-20), nil
}

func formatBinaryTime(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[11 : 11+length], nil
	}
	var dst []byte // return value

	switch length {
	case
		8,                      // time (can be up to 10 when negative and 100+ hours)
		10, 11, 12, 13, 14, 15: // time with fractional seconds
	default:
		return nil, fmt.Errorf("illegal TIME length %d", length)
	}
	switch len(src) {
	case 8, 12:
	default:
		return nil, fmt.Errorf("invalid TIME packet length %d", len(src))
	}
	// +2 to enable negative time and 100+ hours
	dst = make([]byte, 0, length+2)
	if src[0] == 1 {
		dst = append(dst, '-')
	}
	days := binary.LittleEndian.Uint32(src[1:5])
	hours := int64(days)*24 + int64(src[5])

	if hours >= 100 {
		dst = strconv.AppendInt(dst, hours, 10)
	} else {
		dst = append(dst, digits10[hours], digits01[hours])

View on GitHub (pinned to 03d76c7e07)