go-sql-driver/mysql · error

illegal TIME length %d

Error message

illegal TIME length %d

What it means

Thrown by formatBinaryTime (utils.go:460) when the DECLARED length of a binary TIME column is not in {8, 10, 11, 12, 13, 14, 15} — the legal binary TIME lengths (8 bytes base, plus fractional-second bytes). MySQL TIME can be negative or exceed 24h, and the driver only knows how to render these specific declared lengths.

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 c426bd9379)

Solutions

  1. Inspect the column with SHOW COLUMNS / DESCRIBE and confirm it is a TIME type with a sane fractional precision.
  2. Reproduce against the text protocol (CAST(col AS CHAR) or plain Query without ?) to isolate binary-path corruption.
  3. Bypass proxies/tunnels and connect directly to MySQL.
  4. If reproducible on vanilla MySQL, report upstream with the length value and column DDL.

Example fix

// before
var d time.Duration
db.QueryRow("SELECT elapsed FROM t WHERE id=?", id).Scan(&d)

// after
var raw string
if err := db.QueryRow("SELECT CAST(elapsed AS CHAR) FROM t WHERE id=?", id).Scan(&raw); err == nil {
    // parse 'HH:MM:SS' or '-HH:MM:SS' into a duration manually
}
Defensive patterns

Strategy: try-catch

Try / catch

if err := rows.Scan(...); err != nil {
    if strings.Contains(err.Error(), "illegal TIME length") {
        // declared TIME length unsupported; read via CAST AS CHAR
    }
}

Prevention

When it happens

Trigger: Scanning a MySQL TIME column from a binary-protocol row whose column metadata declares an unsupported length — corrupted column definition, non-MySQL server, or packet mangling. Fires during Scan on prepared-statement rows that touch a TIME column.

Common situations: A buggy proxy/router rewriting binary result metadata; a non-conforming MySQL fork; version skew; rare buffer corruption. The message reports the offending declared length.

Related errors


AI-assisted analysis of go-sql-driver/mysql@c426bd9379 (2026-08-04). Data as JSON: /data/errors/16d3f5c15a746c80.json. Report an issue: GitHub.