go-sql-driver/mysql · error

invalid DATETIME packet length %d

Error message

invalid DATETIME packet length %d

What it means

Thrown by parseBinaryDateTime (utils.go:265) — the fallback of its switch — when decoding a BINARY-protocol DATETIME value whose declared length `num` is not one of {0, 4, 7, 11}. MySQL's binary protocol only sends DATETIME payloads of those exact lengths (date / date+time / date+time+fraction), so any other length is a protocol violation.

Source

Thrown at utils.go:265

			int(data[4]),                              // hour
			int(data[5]),                              // minutes
			int(data[6]),                              // seconds
			0,
			loc,
		), nil
	case 11:
		return time.Date(
			int(binary.LittleEndian.Uint16(data[:2])), // year
			time.Month(data[2]),                       // month
			int(data[3]),                              // day
			int(data[4]),                              // hour
			int(data[5]),                              // minutes
			int(data[6]),                              // seconds
			int(binary.LittleEndian.Uint32(data[7:11]))*1000, // nanoseconds
			loc,
		), nil
	}
	return nil, fmt.Errorf("invalid DATETIME packet length %d", num)
}

func appendDateTime(buf []byte, t time.Time, timeTruncate time.Duration) ([]byte, error) {
	if timeTruncate > 0 {
		t = t.Truncate(timeTruncate)
	}

	year, month, day := t.Date()
	hour, min, sec := t.Clock()
	nsec := t.Nanosecond()

	if year < 1 || year > 9999 {
		return buf, errors.New("year is not in the range [1, 9999]: " + strconv.Itoa(year)) // use errors.New instead of fmt.Errorf to avoid year escape to heap
	}
	year100 := year / 100
	year1 := year % 100

	var localBuf [len("2006-01-02T15:04:05.999999999")]byte // does not escape

View on GitHub (pinned to c426bd9379)

Solutions

  1. Run the same prepared-statement query in the mysql CLI (or with a plain db.Query without ? to force the text protocol) and see if it succeeds.
  2. Inspect the network path for proxies/routers that may mangle binary row data; connect directly to MySQL.
  3. Confirm server version and that the column is a real DATETIME/TIMESTAMP.
  4. If reproducible only via a specific proxy, file a bug with the proxy vendor; meanwhile disable compression and proxies.

Example fix

// before: binary protocol via prepared statement
rows, err := db.Query("SELECT created_at FROM orders WHERE id = ?", id)

// after: force text protocol to bypass the bad binary path for diagnosis
rows, err := db.Query("SELECT created_at FROM orders WHERE id = " + strconv.Itoa(id))
Defensive patterns

Strategy: try-catch

Try / catch

var t time.Time
if err := rows.Scan(&t); err != nil {
    if strings.Contains(err.Error(), "invalid DATETIME packet length") {
        // binary-protocol corruption; retry once, or fall back to text protocol
    }
}

Prevention

When it happens

Trigger: Reading a DATETIME/TIMESTAMP column over the binary (prepared-statement) protocol where the server advertises an unsupported payload length — e.g. a corrupted or truncated binary row packet, or a non-conforming server/proxy. Fires during Scan on rows from a prepared statement (db.Query with ? placeholders uses the binary protocol).

Common situations: Packet corruption from a flaky network or a misbehaving proxy/router between app and MySQL; an exotic MySQL fork that emits non-standard binary datetime lengths; an old/new server version mismatch; memory/packet buffer corruption.

Related errors


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