jmoiron/sqlx · error

empty slice passed to 'in' query

Error message

empty slice passed to 'in' query

What it means

sqlx.In() builds an 'IN (...)' query by expanding slice arguments into a list of bind vars. If any passed argument is a slice with zero elements, there are no placeholders to expand and the generated SQL would be syntactically invalid, so sqlx refuses to build the query with this error.

Source

Thrown at bind.go:180

	for i, arg := range args {
		if a, ok := arg.(driver.Valuer); ok {
			var err error
			arg, err = a.Value()
			if err != nil {
				return "", nil, err
			}
		}

		if v, ok := asSliceForIn(arg); ok {
			meta[i].length = v.Len()
			meta[i].v = v

			anySlices = true
			flatArgsCount += meta[i].length

			if meta[i].length == 0 {
				return "", nil, errors.New("empty slice passed to 'in' query")
			}
		} else {
			meta[i].i = arg
			flatArgsCount++
		}
	}

	// don't do any parsing if there aren't any slices;  note that this means
	// some errors that we might have caught below will not be returned.
	if !anySlices {
		return query, args, nil
	}

	newArgs := make([]interface{}, 0, flatArgsCount)

	var buf strings.Builder
	buf.Grow(len(query) + len(", ?")*flatArgsCount)

View on GitHub (pinned to 41dac167fd)

Solutions

  1. Short-circuit before calling In: if len(slice)==0, skip the query or return an empty result set
  2. Construct the query differently for the empty case (e.g. query with a guaranteed-false predicate like "WHERE 1=0")
  3. Drop the IN clause entirely when the slice is empty
  4. Use Go's slices/len validation in a helper wrapper around sqlx.In

Example fix

// before
query, args, err := sqlx.In("SELECT * FROM users WHERE id IN (?)", ids)
// after
if len(ids) == 0 {
    return nil, nil // or "SELECT * FROM users WHERE 1=0"
}
query, args, err := sqlx.In("SELECT * FROM users WHERE id IN (?)", ids)
Defensive patterns

Strategy: validation

Validate before calling

func safeIn(query string, args ...interface{}) (string, []interface{}, error) {
    for _, a := range args {
        if rv := reflect.ValueOf(a); rv.Kind() == reflect.Slice && rv.Len() == 0 {
            return "", nil, fmt.Errorf("empty slice for query %q", query)
        }
    }
    return sqlx.In(query, args...)
}

Type guard

func isEmptySliceArg(a interface{}) bool {
    if a == nil { return false }
    v := reflect.ValueOf(a)
    return v.Kind() == reflect.Slice && v.Len() == 0
}

Try / catch

query, args, err := sqlx.In("SELECT * FROM t WHERE id IN (?)", ids)
if err != nil {
    if strings.Contains(err.Error(), "empty slice passed to 'in' query") {
        return nil, nil // empty result is a valid outcome
    }
    return nil, err
}

Prevention

When it happens

Trigger: Calling sqlx.In (or QueryInx/NamedQuery variants that route through it) with an empty slice among the variadic args, e.g. sqlx.In("SELECT * FROM t WHERE id IN (?)", ids) where len(ids)==0. The check fires in the bind.go meta-building loop when meta[i].length==0.

Common situations: Filtering by a user-selected list that turned out empty (no checkboxes selected, no results from a previous query feeding an IN clause). Very common in list/filter endpoints where the filter collection starts empty.

Related errors


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