go-sql-driver/mysql · error
year is not in the range [1, 9999]: %s
Error message
year is not in the range [1, 9999]: %s
What it means
Returned by appendDateTime (utils.go:278) when serializing a time.Time to send to MySQL and the year is outside [1,9999]. MySQL's DATE/DATETIME types cannot represent year 0 or years >= 10000, so the driver refuses to format them. It is raised while building interpolated query arguments (connection.go:380) and binary protocol values (packets.go:1211).
Source
Thrown at utils.go:278
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
localBuf[0], localBuf[1], localBuf[2], localBuf[3] = digits10[year100], digits01[year100], digits10[year1], digits01[year1]
localBuf[4] = '-'
localBuf[5], localBuf[6] = digits10[month], digits01[month]
localBuf[7] = '-'
localBuf[8], localBuf[9] = digits10[day], digits01[day]
if hour == 0 && min == 0 && sec == 0 && nsec == 0 {
return append(buf, localBuf[:10]...), nil
}
localBuf[10] = ' '
localBuf[11], localBuf[12] = digits10[hour], digits01[hour]
localBuf[13] = ':'View on GitHub (pinned to 03d76c7e07)
Solutions
- Send NULL for absent dates using sql.NullTime{Valid:false} or a nil *time.Time instead of time.Time{}.
- Clamp/validate the year to [1,9999] before binding: reject or truncate out-of-range values.
- Fix the upstream computation producing year 0 or year >= 10000.
- For genuine out-of-range dates, store as a string column and format yourself.
Example fix
// before: zero time.Time (year 1) intended as 'no date'
db.Exec("INSERT INTO t(d) VALUES(?)", time.Time{})
// -> "year is not in the range [1, 9999]: 1" on some paths / far-future overflow
// after: express absence as NULL
var d sql.NullTime // Valid: false -> NULL
db.Exec("INSERT INTO t(d) VALUES(?)", d) Defensive patterns
Strategy: validation
Validate before calling
// Validate a time.Time before binding it as a MySQL temporal value.
func validForMySQL(t time.Time) bool {
y := t.Year()
return y >= 1 && y <= 9999
} Type guard
func isYearOutOfRange(err error) bool {
return err != nil && strings.HasPrefix(err.Error(), "year is not in the range [1, 9999]")
} Try / catch
if !validForMySQL(t) {
// send NULL instead, or clamp/fix the value before binding
arg := sql.NullTime{}
db.ExecContext(ctx, "INSERT INTO t(d) VALUES(?)", arg)
} Prevention
- Use sql.NullTime (or *time.Time) to represent absent dates instead of time.Time{}.
- Range-check user/computed dates before persisting.
- Watch for date-overflow arithmetic near year 9999.
When it happens
Trigger: Binding a time.Time whose year is 0 (a non-zero-check zero value, or year 0001 which maps to Go year 1 boundary), or a far-future date (year >= 10000) as a query parameter that gets formatted via appendDateTime.
Common situations: Passing the zero time.Time (year 1) intending NULL but not using sql.NullTime; date arithmetic overflow; mock/test data with absurd years; a computed expiry date rolling past 9999.
Related errors
- invalid time bytes: %s
- not [0-9]
- bad value for field: `%c`
- invalid DATETIME packet length %d
- illegal %s length %d
AI-assisted analysis of go-sql-driver/mysql@03d76c7e07 (2026-08-07).
Data as JSON: /api/errors/f2771d03901b89f6.
Report an issue: GitHub.