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

  1. 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')).
  2. If raw values must be preserved, scan into a string or []byte / sql.NullString instead of time.Time so parseDateTime is bypassed.
  3. Switch the DSN to parseTime=false so the driver returns datetime columns as raw []byte, then parse defensively in your own code.
  4. 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

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), nil

View on GitHub (pinned to 03d76c7e07)