{"record":{"id":"1b8230956a1bdafa","repo":"go-sql-driver/mysql","slug":"invalid-datetime-packet-length-d","errorCode":null,"errorMessage":"invalid DATETIME packet length %d","messagePattern":"invalid DATETIME packet length (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"utils.go","lineNumber":265,"sourceCode":"\t\t\tint(data[4]),                              // hour\n\t\t\tint(data[5]),                              // minutes\n\t\t\tint(data[6]),                              // seconds\n\t\t\t0,\n\t\t\tloc,\n\t\t), nil\n\tcase 11:\n\t\treturn time.Date(\n\t\t\tint(binary.LittleEndian.Uint16(data[:2])), // year\n\t\t\ttime.Month(data[2]),                       // month\n\t\t\tint(data[3]),                              // day\n\t\t\tint(data[4]),                              // hour\n\t\t\tint(data[5]),                              // minutes\n\t\t\tint(data[6]),                              // seconds\n\t\t\tint(binary.LittleEndian.Uint32(data[7:11]))*1000, // nanoseconds\n\t\t\tloc,\n\t\t), nil\n\t}\n\treturn nil, fmt.Errorf(\"invalid DATETIME packet length %d\", num)\n}\n\nfunc appendDateTime(buf []byte, t time.Time, timeTruncate time.Duration) ([]byte, error) {\n\tif timeTruncate > 0 {\n\t\tt = t.Truncate(timeTruncate)\n\t}\n\n\tyear, month, day := t.Date()\n\thour, min, sec := t.Clock()\n\tnsec := t.Nanosecond()\n\n\tif year < 1 || year > 9999 {\n\t\treturn 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\n\t}\n\tyear100 := year / 100\n\tyear1 := year % 100\n\n\tvar localBuf [len(\"2006-01-02T15:04:05.999999999\")]byte // does not escape","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/go-sql-driver/mysql/blob/03d76c7e07908e255ce62d126d07ede3f2365d86/utils.go#L247-L283","documentation":"Raised by parseBinaryDateTime (utils.go:265) when decoding a DATETIME/TIMESTAMP/DATE value received via the MySQL BINARY protocol (prepared statements). The driver expects the value payload length (num) to be 0 (zero), 4 (DATE), 7 (DATETIME no fractional), or 11 (DATETIME with fractional); any other length is protocol corruption and cannot be decoded.","triggerScenarios":"Using prepared statements (db.Query/QueryRow with args, or Prepare+Exec/Query) to SELECT a DATETIME/TIMESTAMP/DATE column with parseTime=true, where the server sends a binary value whose length is not 0/4/7/11. Reached via parseBinaryDateTime at packets.go:1416. Typical with non-conforming MySQL-compatible servers (some proxies, forks, or older protocol implementations) or when a packet desync occurred.","commonSituations":"Connecting through a misbehaving proxy/sharder (Vitess, old Aurora, DB2 compatibility layers), a server speaking an incompatible or corrupted binary protocol, memory/packet corruption, or a driver/server version mismatch after a server upgrade.","solutions":["Verify the MySQL-compatible server and any proxy in the path speak the standard binary protocol; upgrade or patch the offending component.","Avoid prepared statements for that query (use db.Exec/Query with interpolation-free text query path) to fall back to the text protocol and parseDateTime.","Confirm the column is truly a temporal type and not an exotic type the server encodes differently.","Capture the packet (MySQL general log / tcpdump) and file a bug against the proxy/server if the length is genuinely invalid."],"exampleFix":"// before: prepared-statement binary path hits bad length\ndb.QueryRow(`SELECT created_at FROM t WHERE id=?`, id).Scan(&createdAt)\n\n// after: bypass binary protocol by scanning raw, or use text query\nvar raw []byte\ndb.QueryRow(`SELECT created_at FROM t WHERE id=?`, id).Scan(&raw)\nt, _ := time.Parse(\"2006-01-02 15:04:05\", string(raw))","handlingStrategy":"try-catch","validationCode":"// You cannot pre-validate server-sent binary payloads; instead detect a\n// non-conforming server/proxy before relying on prepared-statement datetime.\nfunc probeBinaryDateTime(db *sql.DB) error {\n    var raw []byte\n    return db.QueryRow(`SELECT CAST(NOW() AS DATETIME)`).Scan(&raw)\n}","typeGuard":"func isStandardBinaryDatetimeLen(n int) bool {\n    switch n {\n    case 0, 4, 7, 11:\n        return true\n    }\n    return false\n}","tryCatchPattern":"var t time.Time\nif err := db.QueryRow(q, id).Scan(&t); err != nil {\n    if strings.Contains(err.Error(), \"invalid DATETIME packet length\") {\n        // fall back: read raw bytes and parse text form\n        var raw []byte\n        _ = db.QueryRow(`SELECT CAST(ts AS CHAR) FROM t WHERE id=?`, id).Scan(&raw)\n        t, _ = time.Parse(\"2006-01-02 15:04:05\", string(raw))\n    }\n}","preventionTips":["Pin known-good MySQL/Proxy versions; test datetime reads after server upgrades.","Keep a text-protocol fallback query for temporal columns when targeting proxies.","Report invalid binary lengths to the proxy/server vendor with a packet capture."],"tags":["mysql","datetime","binary-protocol","prepared-statements","protocol-error","parsetime"],"backgroundTag":null,"analyzedSha":"03d76c7e07908e255ce62d126d07ede3f2365d86","analyzedAt":"2026-08-07T10:39:17.340Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}