jmoiron/sqlx · error

number of bindVars exceeds arguments

Error message

number of bindVars exceeds arguments

What it means

sqlx.In() walks the query string looking for '?' bind vars and expands each one using the corresponding argument. If the query contains more '?' placeholders than arguments were passed, the excess placeholders cannot be filled and this programmer error is reported (unlike database/sql, sqlx catches this eagerly at bind time).

Source

Thrown at bind.go:207

	// 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)

	var arg, offset int

	for i := strings.IndexByte(query[offset:], '?'); i != -1; i = strings.IndexByte(query[offset:], '?') {
		if arg >= len(meta) {
			// if an argument wasn't passed, lets return an error;  this is
			// not actually how database/sql Exec/Query works, but since we are
			// creating an argument list programmatically, we want to be able
			// to catch these programmer errors earlier.
			return "", nil, errors.New("number of bindVars exceeds arguments")
		}

		argMeta := meta[arg]
		arg++

		// not a slice, continue.
		// our questionmark will either be written before the next expansion
		// of a slice or after the loop when writing the rest of the query
		if argMeta.length == 0 {
			offset = offset + i + 1
			newArgs = append(newArgs, argMeta.i)
			continue
		}

		// write everything up to and including our ? character
		buf.WriteString(query[:offset+i+1])

		for si := 1; si < argMeta.length; si++ {

View on GitHub (pinned to 41dac167fd)

Solutions

  1. Count '?' placeholders in the query and make sure each has exactly one argument
  2. Remove the extra '?' or add the missing argument
  3. Prefer named parameters via NamedQuery/PrepareNamed to avoid positional mismatches
  4. Note '?' inside quoted string literals are still counted — escape or remove them

Example fix

// before
query, args, err := sqlx.In("SELECT * FROM users WHERE id IN (?) AND status=?", ids)
// after
query, args, err := sqlx.In("SELECT * FROM users WHERE id IN (?) AND status=?", ids, status)
Defensive patterns

Strategy: validation

Validate before calling

if strings.Count(query, "?") != len(args) {
    return "", nil, fmt.Errorf("query has %d placeholders but %d args", strings.Count(query, "?"), 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(), "number of bindVars exceeds arguments") {
        log.Printf("placeholder/arg mismatch in query: %s", q)
    }
    return err
}

Prevention

When it happens

Trigger: Calling sqlx.In/QueryInx with a query string containing more '?' characters than variadic arguments, e.g. sqlx.In("SELECT * FROM t WHERE a=? AND b=?", valA) — the second '?' finds arg >= len(meta).

Common situations: Hand-edited queries where a condition was removed from the code but not the SQL string; typos producing stray '?' characters (including inside string literals); merging query fragments incorrectly.

Related errors


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