jmoiron/sqlx · error

length of array is 0: %#v

Error message

length of array is 0: %#v

What it means

When binding an array/slice argument with the array-style bind type (e.g. Postgres' expanded ?* binding), bindArray needs at least one element to expand parameters for. A zero-length array yields no parameter groups, which would produce an invalid/empty IN clause, so sqlx errors out instead of emitting broken SQL.

Source

Thrown at named.go:283

		buffer.WriteString(bound[openingBracketIndex:closingBracketIndex])
	}
	buffer.WriteString(bound[closingBracketIndex:])
	return buffer.String()
}

// bindArray binds a named parameter query with fields from an array or slice of
// structs argument.
func bindArray(bindType int, query string, arg interface{}, m *reflectx.Mapper) (string, []interface{}, error) {
	// do the initial binding with QUESTION;  if bindType is not question,
	// we can rebind it at the end.
	bound, names, err := compileNamedQuery([]byte(query), QUESTION)
	if err != nil {
		return "", []interface{}{}, err
	}
	arrayValue := reflect.ValueOf(arg)
	arrayLen := arrayValue.Len()
	if arrayLen == 0 {
		return "", []interface{}{}, fmt.Errorf("length of array is 0: %#v", arg)
	}
	var arglist = make([]interface{}, 0, len(names)*arrayLen)
	for i := 0; i < arrayLen; i++ {
		elemArglist, err := bindAnyArgs(names, arrayValue.Index(i).Interface(), m)
		if err != nil {
			return "", []interface{}{}, err
		}
		arglist = append(arglist, elemArglist...)
	}
	if arrayLen > 1 {
		bound = fixBound(bound, arrayLen)
	}
	// adjust binding type if we weren't on question
	if bindType != QUESTION {
		bound = Rebind(bindType, bound)
	}
	return bound, arglist, nil
}

View on GitHub (pinned to 41dac167fd)

Solutions

  1. Guard for empty slices before binding and return an empty result or use a query variant without the IN clause.
  2. If SQL semantics allow, use a sentinel value (e.g. WHERE (:ids IS NULL) patterns) or `= ANY($1)` with pq.Array.
  3. Ensure the slice is populated before calling the named API.
  4. Handle the error and skip executing the query when there is nothing to bind.

Example fix

// before
var ids []int64
rows, _ := db.NamedQuery("SELECT * FROM t WHERE id IN (:ids)", map[string]interface{}{"ids": ids})
// after
if len(ids) == 0 {
    return nil, nil // or use always-false query
}
rows, _ := db.NamedQuery("SELECT * FROM t WHERE id IN (:ids)", map[string]interface{}{"ids": ids})
Defensive patterns

Strategy: validation

Validate before calling

if len(ids) == 0 {
	return nil, nil // skip the IN-clause query entirely
}

Try / catch

q, args, err := db.BindNamed(query, arg)
if err != nil && strings.Contains(err.Error(), "length of array is 0") {
	// empty input: return empty result instead of executing
	return nil, err
}

Prevention

When it happens

Trigger: Binding a slice/array via BindNamed/Named-style APIs where the mapped bind type expands arrays (e.g. BindType returns Question for slices?) and the slice has len==0 — e.g. NamedExec with an empty slice for an IN clause.

Common situations: SELECT ... IN (:ids) with ids from an empty filter result; slice nil/empty because an earlier query returned nothing; passing an empty array-typed struct field.

Related errors


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