go-sql-driver/mysql · error

unsupported type %T, a %s

Error message

unsupported type %T, a %s

What it means

The driver's converter (statement.go:211) fell through its entire type switch: the value's reflect.Kind is none of pointer, integer, unsigned, float, bool, slice, or string, and the type does not implement driver.Valuer. This covers structs, maps, arrays, chans, and funcs that the driver cannot serialize.

Source

Thrown at statement.go:211

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return rv.Uint(), nil
	case reflect.Float32, reflect.Float64:
		return rv.Float(), nil
	case reflect.Bool:
		return rv.Bool(), nil
	case reflect.Slice:
		switch t := rv.Type(); {
		case t == jsonType:
			return v, nil
		case t.Elem().Kind() == reflect.Uint8:
			return rv.Bytes(), nil
		default:
			return nil, fmt.Errorf("unsupported type %T, a slice of %s", v, t.Elem().Kind())
		}
	case reflect.String:
		return rv.String(), nil
	}
	return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind())
}

var valuerReflectType = reflect.TypeFor[driver.Valuer]()

// callValuerValue returns vr.Value(), with one exception:
// If vr.Value is an auto-generated method on a pointer type and the
// pointer is nil, it would panic at runtime in the panicwrap
// method. Treat it like nil instead.
//
// This is so people can implement driver.Value on value types and
// still use nil pointers to those types to mean nil/NULL, just like
// string/*string.
//
// This is an exact copy of the same-named unexported function from the
// database/sql package.
func callValuerValue(vr driver.Valuer) (v driver.Value, err error) {
	if rv := reflect.ValueOf(vr); rv.Kind() == reflect.Ptr &&
		rv.IsNil() &&

View on GitHub (pinned to c426bd9379)

Solutions

  1. Marshal structs/maps to JSON and pass the resulting []byte.
  2. Implement driver.Valuer on the custom type so Value() returns a base driver.Value.
  3. Extract the specific primitive field(s) needed and pass those individually.

Example fix

// before
db.Exec("INSERT INTO t(data) VALUES(?)", myStruct)

// after
b, _ := json.Marshal(myStruct)
db.Exec("INSERT INTO t(data) VALUES(?)", b)
Defensive patterns

Strategy: type-guard

Validate before calling

func isSupportedArg(v any) bool {
    if v == nil { return true }
    if _, ok := v.(driver.Valuer); ok { return true }
    switch reflect.ValueOf(v).Kind() {
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
        reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
        reflect.Float32, reflect.Float64, reflect.Bool, reflect.String:
        return true
    }
    return false
}

Type guard

func supportedKind(k reflect.Kind) bool {
    switch k {
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
        reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
        reflect.Float32, reflect.Float64, reflect.Bool, reflect.String:
        return true
    }
    return false
}

Try / catch

if _, err := db.Exec(q, v); err != nil {
    if strings.Contains(err.Error(), "unsupported type") {
        // marshal to []byte or implement driver.Valuer
    }
}

Prevention

When it happens

Trigger: Passing a struct or map directly as an argument; passing a chan/func value; a nil interface wrapping an unhandled concrete type.

Common situations: Expecting automatic JSON serialization of a struct; passing a whole map as a parameter; submitting a domain object without extracting primitive fields.

Related errors


AI-assisted analysis of go-sql-driver/mysql@c426bd9379 (2026-08-04). Data as JSON: /data/errors/c7a5dee657dea184.json. Report an issue: GitHub.