jmoiron/sqlx · error

sqlx.bindNamedMapper: unsupported map type: %T

Error message

sqlx.bindNamedMapper: unsupported map type: %T

What it means

bindNamedMapper dispatches on the reflect.Kind of the argument: string-keyed maps go to bindMap, arrays/slices to bindArray, everything else to bindStruct. If the argument is a map whose key type is not string AND cannot be converted to map[string]interface{} (convertMapStringInterface fails), sqlx cannot bind it and reports the unsupported type. Non-string map keys have no defined named-parameter semantics.

Source

Thrown at named.go:428

func BindNamed(bindType int, query string, arg interface{}) (string, []interface{}, error) {
	return bindNamedMapper(bindType, query, arg, mapper())
}

// Named takes a query using named parameters and an argument and
// returns a new query with a list of args that can be executed by
// a database.  The return value uses the `?` bindvar.
func Named(query string, arg interface{}) (string, []interface{}, error) {
	return bindNamedMapper(QUESTION, query, arg, mapper())
}

func bindNamedMapper(bindType int, query string, arg interface{}, m *reflectx.Mapper) (string, []interface{}, error) {
	t := reflect.TypeOf(arg)
	k := t.Kind()
	switch {
	case k == reflect.Map && t.Key().Kind() == reflect.String:
		m, ok := convertMapStringInterface(arg)
		if !ok {
			return "", nil, fmt.Errorf("sqlx.bindNamedMapper: unsupported map type: %T", arg)
		}
		return bindMap(bindType, query, m)
	case k == reflect.Array || k == reflect.Slice:
		return bindArray(bindType, query, arg, m)
	default:
		return bindStruct(bindType, query, arg, m)
	}
}

// NamedQuery binds a named query and then runs Query on the result using the
// provided Ext (sqlx.Tx, sqlx.Db).  It works with both structs and with
// map[string]interface{} types.
func NamedQuery(e Ext, query string, arg interface{}) (*Rows, error) {
	q, args, err := bindNamedMapper(BindType(e.DriverName()), query, arg, mapperFor(e))
	if err != nil {
		return nil, err
	}
	return e.Queryx(q, args...)

View on GitHub (pinned to 41dac167fd)

Solutions

  1. Convert the map to map[string]interface{} (stringify keys) before binding.
  2. Use a struct argument instead so bindStruct/reflectx handles field mapping.
  3. For slices/arrays of values, pass them as the array/slice branch expects.
  4. If using custom key types that are ~string, convert explicitly: m2 := make(map[string]interface{}, len(m)).

Example fix

// before
args := map[int]interface{}{1: "a"}
db.NamedExec(q, args) // unsupported map type
// after
args := map[string]interface{}{"1": "a"}
db.NamedExec(q, args)
Defensive patterns

Strategy: validation

Validate before calling

func bindableMap(arg interface{}) error {
	t := reflect.TypeOf(arg)
	if t == nil || t.Kind() != reflect.Map {
		return nil // struct/slice handled elsewhere
	}
	if t.Key().Kind() != reflect.String {
		return fmt.Errorf("map key %s not string; convert to map[string]interface{}", t.Key())
	}
	return nil
}

Type guard

func isStringKeyedMap(v interface{}) bool {
	t := reflect.TypeOf(v)
	return t != nil && t.Kind() == reflect.Map && t.Key().Kind() == reflect.String
}

Try / catch

q, args, err := db.BindNamed(query, arg)
if err != nil && strings.Contains(err.Error(), "unsupported map type") {
	// convert arg to map[string]interface{} and retry
	return err
}

Prevention

When it happens

Trigger: Passing a map with non-string keys (e.g. map[int]interface{}, map[CustomKey]T, or map with non-basic key type) to BindNamed/Named/NamedExec/NamedQuery (and their Context variants).

Common situations: Using map[int]string lookup tables as bind args; map keys defined as named string types wrapped oddly or maps the conversion helper rejects (e.g. map[interface{}]interface{} from YAML); refactored code passing a struct map with custom key struct types.

Related errors


AI-assisted analysis of jmoiron/sqlx@41dac167fd (2026-09-03). Data as JSON: /api/errors/aaab443a30358421. Report an issue: GitHub.