go-sql-driver/mysql · error
bad value for field: `%c`
Error message
bad value for field: `%c`
What it means
Raised by parseDateTime (utils.go:121) while decoding a TEXT-protocol DATE/DATETIME/TIMESTAMP value. After the 4-digit year is parsed, the byte at offset 4 must be '-' (the year/month separator in 'YYYY-MM-DD HH:MM:SS'). Any other byte means the value returned by the server is not a well-formed MySQL date string, so the driver refuses to coerce it into a Go time.Time.
Solutions
- Normalize the stored data so every date value matches 'YYYY-MM-DD[ HH:MM:SS[.ffffff]]' (e.g. UPDATE ... SET col = STR_TO_DATE(col, '%Y/%m/%d')).
- If raw values must be preserved, scan into a string or []byte / sql.NullString instead of time.Time so parseDateTime is bypassed.
- Switch the DSN to parseTime=false so the driver returns datetime columns as raw []byte, then parse defensively in your own code.
- For non-DATE columns holding formatted dates, fix the schema/column type or the SELECT so the value arrives as a real DATE.
Example fix
// before: assumes well-formed date
var t time.Time
rows.Scan(&t)
// after: tolerate non-standard separators by reading raw then parsing
var raw string
if err := rows.Scan(&raw); err != nil { return err }
t, err := time.Parse("2006/01/02 15:04:05", raw)
if err != nil { /* handle / skip row */ } Defensive patterns
Strategy: validation
Validate before calling
// Validate a text datetime value before relying on driver coercion.
func isValidMySQLDateText(s string) bool {
if len(s) != 10 && !(len(s) >= 19 && len(s) <= 26) {
return false
}
if len(s) >= 5 && s[4] != '-' {
return false
}
_, err := time.Parse("2006-01-02", s[:10])
return err == nil
} Type guard
func isCanonicalDateText(b []byte) bool {
return len(b) >= 10 && b[4] == '-' && b[7] == '-'
} Try / catch
// For parseTime=true scans, check the returned error.
var t time.Time
if err := rows.Scan(&t); err != nil {
if strings.Contains(err.Error(), "bad value for field") {
// log/repair the offending row; rescan as string to inspect
}
} Prevention
- Store dates only via parameterized time.Time or real DATE columns, never formatted strings.
- Run SELECT ... WHERE col NOT REGEXP '^[0-9]{4}-[0-9]{2}-[0-9]{2}' to find malformed dates.
- Keep parseTime=true only when your data is trusted canonical; otherwise scan into string.
- Enforce sql_mode=STRICT_TRANS_TABLES to prevent invalid dates being inserted.
When it happens
Trigger: Scanning a DATE/DATETIME/TIMESTAMP column into a Go time.Time (requires DSN parseTime=true), or scanning into sql.NullTime, when the server's text value has a non '-' byte at position 4 — e.g. '2020/01/15 ...' (slash), '2020.01.15 ...' (dot), or truncated/corrupted rows. The path is rows.readRows -> parseDateTime (packets.go:875) or nulltime.Scan (nulltime.go:52).
Common situations: Applications that previously stored dates with locale-specific or slash separators, columns declared as VARCHAR/TEXT holding human-formatted dates but read as datetime, an sql_mode that allowed bad dates to be inserted, or data migrated from a non-MySQL source without normalization.
Related errors
AI-assisted analysis of go-sql-driver/mysql@03d76c7e07 (2026-08-07).
Data as JSON: /api/errors/90c6e55ad06c9cf2.
Report an issue: GitHub.
Appendix: source
Thrown at utils.go:121
/******************************************************************************
* Time related utils *
******************************************************************************/
func parseDateTime(b []byte, loc *time.Location) (time.Time, error) {
const base = "0000-00-00 00:00:00.000000"
switch len(b) {
case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM"
if string(b) == base[:len(b)] {
return time.Time{}, nil
}
year, err := parseByteYear(b)
if err != nil {
return time.Time{}, err
}
if b[4] != '-' {
return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[4])
}
m, err := parseByte2Digits(b[5], b[6])
if err != nil {
return time.Time{}, err
}
month := time.Month(m)
if b[7] != '-' {
return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[7])
}
day, err := parseByte2Digits(b[8], b[9])
if err != nil {
return time.Time{}, err
}
if len(b) == 10 {
return time.Date(year, month, day, 0, 0, 0, 0, loc), nilView on GitHub (pinned to 03d76c7e07)