go-sql-driver/mysql · error

unsupported type %T, a slice of

Error message

unsupported type %T, a slice of %s

What it means

ConvertValue only accepts byte slices ([]byte) and json.RawMessage among slice types; a slice of any other element kind is unsupported and rejected, naming the element Kind in the message.

Solutions

  1. Serialize the slice yourself (e.g. JSON-encode to []byte/string) before passing it.
  2. Expand the slice into one '?' placeholder per element and pass each element separately.
  3. Store the collection as JSON/TEXT in a single column.

Example fix

// before
stmt.Exec([]string{"a","b"}) // SQL: INSERT INTO t(tags) VALUES (?)
// after (JSON-encode)
b, _ := json.Marshal([]string{"a","b"})
stmt.Exec(string(b))
Defensive patterns

Strategy: validation

Validate before calling

// Reject non-byte slices before Exec.
func okArg(v any) bool {
    rv := reflect.ValueOf(v)
    if rv.Kind() == reflect.Slice && rv.Type().Elem().Kind() != reflect.Uint8 {
        return false
    }
    return true
}

Type guard

func isSupportedSlice(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{})
}

Try / catch

// Returned from Exec/Query for unsupported slice arguments.
if _, err := stmt.Exec(args...); err != nil {
    if strings.Contains(err.Error(), "a slice of") {
        // JSON-encode or expand the slice
    }
}

Prevention

When it happens

Trigger: Passing []int, []string, []MyType, or any non-byte slice as a prepared-statement argument.

Common situations: Expecting the driver to expand/serialize arrays automatically; passing a slice where a single value was intended.

Related errors


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

Appendix: 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 03d76c7e07)