jmoiron/sqlx · error
number of bindVars less than number arguments
Error message
number of bindVars less than number arguments
What it means
After walking the query and consuming bind vars for each argument, sqlx.In() checks whether any arguments remain unused. If so, the query has fewer '?' placeholders than arguments, so the leftover arguments cannot be bound and this error is returned.
Source
Thrown at bind.go:240
// write everything up to and including our ? character
buf.WriteString(query[:offset+i+1])
for si := 1; si < argMeta.length; si++ {
buf.WriteString(", ?")
}
newArgs = appendReflectSlice(newArgs, argMeta.v, argMeta.length)
// slice the query and reset the offset. this avoids some bookkeeping for
// the write after the loop
query = query[offset+i+1:]
offset = 0
}
buf.WriteString(query)
if arg < len(meta) {
return "", nil, errors.New("number of bindVars less than number arguments")
}
return buf.String(), newArgs, nil
}
func appendReflectSlice(args []interface{}, v reflect.Value, vlen int) []interface{} {
switch val := v.Interface().(type) {
case []interface{}:
args = append(args, val...)
case []int:
for i := range val {
args = append(args, val[i])
}
case []string:
for i := range val {
args = append(args, val[i])
}
default:View on GitHub (pinned to 41dac167fd)
Solutions
- Remove unused arguments or add the missing '?' placeholders
- Make the IN expansion explicit: each slice arg needs its own '?' in the query
- Generate the WHERE clause programmatically so placeholders and args stay in sync
- Switch to NamedQuery with a struct/map so argument wiring is by name
Example fix
// before
query, args, err := sqlx.In("SELECT * FROM users WHERE id IN (?)", ids, status)
// after
query, args, err := sqlx.In("SELECT * FROM users WHERE id IN (?) AND status=?", ids, status) Defensive patterns
Strategy: validation
Validate before calling
placeholders := strings.Count(query, "?")
if placeholders != len(args) {
return "", nil, fmt.Errorf("expected %d args, got %d", placeholders, len(args))
}
q, a, err := sqlx.In(query, args...) Try / catch
query, args, err := sqlx.In(q, args...)
if err != nil {
if strings.Contains(err.Error(), "less than number arguments") {
log.Printf("dropped args for query %s: got %d", q, len(args))
}
return err
} Prevention
- Build dynamic WHERE clauses programmatically so placeholders and args stay in sync
- Delete arguments when removing conditions during refactors
- Test-generated queries with fixed arg counts in CI
When it happens
Trigger: Calling sqlx.In with more arguments than '?' placeholders in the query, e.g. sqlx.In("SELECT * FROM t WHERE a=?", a, b) — after binding, arg < len(meta).
Common situations: Appending optional conditions in code without updating the query string; passing extra args after refactoring; building dynamic SQL where a condition was dropped but its argument kept.
Related errors
- number of bindVars exceeds arguments
- empty slice passed to 'in' query
- unexpected `:` while reading named param at
- sql: RawBytes isn't allowed on Row.Scan
- must pass a pointer, not a value, to StructScan destination
AI-assisted analysis of jmoiron/sqlx@41dac167fd (2026-09-03).
Data as JSON: /api/errors/f170efc75f329060.
Report an issue: GitHub.