go-sql-driver/mysql · error

unsupported type %T, a slice of %s

Error message

unsupported type %T, a slice of %s

What it means

The driver's converter (statement.go:206) encountered a slice whose element type is not byte/uint8 and not json.RawMessage. Only []byte and json.RawMessage slices are accepted; slices of any other element type (e.g. []int, []string) cannot be transmitted as a single parameter.

Source

Thrown at statement.go:206

		} else {
			return c.ConvertValue(rv.Elem().Interface())
		}
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return rv.Int(), nil
	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.
//

View on GitHub (pinned to c426bd9379)

Solutions

  1. For IN clauses, build the placeholders manually and pass each element as a separate argument.
  2. Marshal non-byte slices to JSON and pass the resulting []byte.
  3. Convert the slice to a formatted string if the SQL expects that representation.

Example fix

// before — []int as a single arg
ids := []int{1, 2, 3}
db.Query("SELECT * FROM t WHERE id IN (?)", ids)

// after — expand placeholders
ph := strings.TrimSuffix(strings.Repeat("?,", len(ids)), ",")
args := make([]any, len(ids))
for i, id := range ids { args[i] = id }
db.Query("SELECT * FROM t WHERE id IN ("+ph+")", args...)
Defensive patterns

Strategy: type-guard

Validate before calling

func isBindableSlice(v any) bool {
    rv := reflect.ValueOf(v)
    if rv.Kind() != reflect.Slice { return true }
    return rv.Type().Elem().Kind() == reflect.Uint8 ||
        rv.Type() == reflect.TypeOf(json.RawMessage(nil))
}

Type guard

func acceptableArg(v any) bool {
    rv := reflect.ValueOf(v)
    if rv.Kind() != reflect.Slice { return true }
    return rv.Type().Elem().Kind() == reflect.Uint8 ||
        rv.Type() == reflect.TypeOf(json.RawMessage(nil))
}

Try / catch

if _, err := db.Query(q, arg); err != nil {
    if strings.Contains(err.Error(), "a slice of") {
        // expand IN-clause placeholders or marshal the slice to []byte
    }
}

Prevention

When it happens

Trigger: Passing []int{1,2,3}, []string{"a","b"}, or any non-byte slice directly as a query/exec argument, often expecting IN-clause expansion or automatic serialization.

Common situations: Expecting the driver to expand a slice into an IN (...) clause (it does not); passing a collection expecting automatic JSON encoding; passing a typed slice from a domain model.

Related errors


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