{"id":"b92efed0f57923fd","repo":"go-sql-driver/mysql","slug":"year-is-not-in-the-range-1-9999-year","errorCode":null,"errorMessage":"year is not in the range [1, 9999]: {year}","messagePattern":"year is not in the range \\[1, 9999\\]: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"utils.go","lineNumber":278,"sourceCode":"\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\n\tlocalBuf[0], localBuf[1], localBuf[2], localBuf[3] = digits10[year100], digits01[year100], digits10[year1], digits01[year1]\n\tlocalBuf[4] = '-'\n\tlocalBuf[5], localBuf[6] = digits10[month], digits01[month]\n\tlocalBuf[7] = '-'\n\tlocalBuf[8], localBuf[9] = digits10[day], digits01[day]\n\n\tif hour == 0 && min == 0 && sec == 0 && nsec == 0 {\n\t\treturn append(buf, localBuf[:10]...), nil\n\t}\n\n\tlocalBuf[10] = ' '\n\tlocalBuf[11], localBuf[12] = digits10[hour], digits01[hour]\n\tlocalBuf[13] = ':'","sourceCodeStart":260,"sourceCodeEnd":296,"githubUrl":"https://github.com/go-sql-driver/mysql/blob/c426bd93799de0f0e094c8f0582872c529d0ed0a/utils.go#L260-L296","documentation":"Thrown by appendDateTime (utils.go:278) when serializing a Go time.Time to the textual form MySQL expects, if the year is < 1 or > 9999. MySQL DATE/DATETIME cannot represent years outside [1, 9999], so the driver refuses to send such a value rather than produce a value the server would reject. This is a CLIENT-side guard, fired when binding a Go time.Time as a parameter.","triggerScenarios":"Executing a parameterized query with a time.Time argument (INSERT/UPDATE/WHERE on a DATE/DATETIME/TIMESTAMP column) whose Year() falls outside [1, 9999]. Common offenders: the Go zero value time.Time{} (year 1, actually OK) but far-future dates, year 0, or negative years; dates produced by bad arithmetic or parsed from malformed input.","commonSituations":"Inserting time.Time{} that was never set and got manipulated; dates from external systems with bogus years (e.g. 0000, 10000+); unit tests with placeholder dates like time.Date(99999, ...); converting a Unix timestamp that overflowed into a wild year.","solutions":["Before binding, validate the time.Time: if t.Year() < 1 || t.Year() > 9999, substitute NULL or a sentinel and skip the value.","Find where the bogus year originates (parsing, arithmetic, default zero value) and fix the upstream producer.","If you legitimately need to store NULL, bind sql.NullTime or nil instead of an out-of-range time.Time.","Add a unit test asserting all inserted dates fall in [1, 9999]."],"exampleFix":"// before\ndb.Exec(\"INSERT INTO t(created) VALUES(?)\", t)\n\n// after: clamp or null out-of-range years\nvar arg any = t\nif t.Year() < 1 || t.Year() > 9999 {\n    arg = nil // store NULL\n}\ndb.Exec(\"INSERT INTO t(created) VALUES(?)\", arg)","handlingStrategy":"validation","validationCode":"// guard any time.Time before binding it as a MySQL parameter\nfunc mysqlTimeArg(t time.Time) any {\n    y := t.Year()\n    if y < 1 || y > 9999 {\n        return nil // store NULL instead\n    }\n    return t\n}\ndb.Exec(\"INSERT INTO t(created) VALUES(?)\", mysqlTimeArg(t))","typeGuard":"// isMySQLOKTime reports whether t fits MySQL's [1,9999] year range\nfunc isMySQLOKTime(t time.Time) bool {\n    y := t.Year()\n    return y >= 1 && y <= 9999\n}","tryCatchPattern":null,"preventionTips":["Always initialize time.Time values; never bind the zero value blindly.","Wrap every time.Time bind in a helper that clamps/nulls out-of-range years.","Validate timestamps from external systems (Unix epoch math, parsing) before storing."],"tags":["datetime","binding","validation","client-side"],"analyzedSha":"c426bd93799de0f0e094c8f0582872c529d0ed0a","analyzedAt":"2026-08-04T21:52:59.219Z","schemaVersion":2}