go-sql-driver/mysql · error

cannot convert type: %T

Error message

cannot convert type: %T

What it means

While encoding prepared-statement arguments for the MySQL binary protocol, writeExecutePacket handles only nil/int64/uint64/float64/bool/[]byte/string/time.Time/json.RawMessage. Any other dynamic type falls through to this default. Normally ConvertValue normalizes Go types first, so this fires when a value bypasses that normalization (custom CheckNamedValue, direct driver use, or a non-canonical driver.Value).

Solutions

  1. Ensure custom Valuers return a standard driver.Value (int64, string, []byte, time.Time, etc.).
  2. Implement driver.Valuer to convert your type into a supported primitive.
  3. Convert the value to a supported Go type before passing it to Exec.

Example fix

// before
type Money struct{ Cents int64 }
// Money not a Valuer -> "cannot convert type: *Money"
stmt.Exec(Money{100})
// after
func (m Money) Value() (driver.Value, error) { return m.Cents, nil }
stmt.Exec(Money{100})
Defensive patterns

Strategy: validation

Validate before calling

// Confirm each arg is a type the wire encoder accepts before Exec.
func wireEncodable(v any) bool {
    switch v.(type) {
    case nil, int64, uint64, float64, bool, []byte, string, time.Time:
        return true
    }
    if _, ok := v.(driver.Valuer); ok { return true }
    return false
}

Type guard

func isDriverValue(v any) bool {
    if v == nil { return true }
    switch v.(type) {
    case int64, float64, bool, []byte, string, time.Time:
        return true
    }
    return false
}

Try / catch

// Returned from Exec/Query; indicates a non-encodable argument type.
if _, err := stmt.Exec(args...); err != nil {
    if strings.Contains(err.Error(), "cannot convert type") {
        // fix the offending arg's type
    }
}

Prevention

When it happens

Trigger: A custom driver.Valuer or driver.NamedValueChecker returning an unusual type; calling the driver directly with a struct/map/raw int; a value that survives database/sql's conversion as a non-standard type.

Common situations: Custom wrapper types that aren't true driver.Value types; passing a domain struct instead of one of its fields.

Related errors


AI-assisted analysis of go-sql-driver/mysql@03d76c7e07 (2026-08-07). Data as JSON: /api/errors/933fd9db908a34ea. Report an issue: GitHub.

Appendix: source

Thrown at packets.go:1223

				var a [64]byte
				var b = a[:0]

				if v.IsZero() {
					b = append(b, "0000-00-00"...)
				} else {
					b, err = appendDateTime(b, v.In(mc.cfg.Loc), mc.cfg.timeTruncate)
					if err != nil {
						return err
					}
				}

				paramValues = appendLengthEncodedInteger(paramValues,
					uint64(len(b)),
				)
				paramValues = append(paramValues, b...)

			default:
				return fmt.Errorf("cannot convert type: %T", arg)
			}
		}

		// Check if param values exceeded the available buffer
		// In that case we must build the data packet with the new values buffer
		if valuesCap != cap(paramValues) {
			data = append(data[:pos], paramValues...)
			mc.buf.store(data) // allow this buffer to be reused
		}

		pos += len(paramValues)
		data = data[:pos]
	}

	err = mc.writePacket(data)
	mc.syncSequence()
	return err
}

View on GitHub (pinned to 03d76c7e07)