{"record":{"id":"f170efc75f329060","repo":"jmoiron/sqlx","slug":"number-of-bindvars-less-than-number-arguments","errorCode":null,"errorMessage":"number of bindVars less than number arguments","messagePattern":"number of bindVars less than number arguments","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"bind.go","lineNumber":240,"sourceCode":"\t\t// write everything up to and including our ? character\n\t\tbuf.WriteString(query[:offset+i+1])\n\n\t\tfor si := 1; si < argMeta.length; si++ {\n\t\t\tbuf.WriteString(\", ?\")\n\t\t}\n\n\t\tnewArgs = appendReflectSlice(newArgs, argMeta.v, argMeta.length)\n\n\t\t// slice the query and reset the offset. this avoids some bookkeeping for\n\t\t// the write after the loop\n\t\tquery = query[offset+i+1:]\n\t\toffset = 0\n\t}\n\n\tbuf.WriteString(query)\n\n\tif arg < len(meta) {\n\t\treturn \"\", nil, errors.New(\"number of bindVars less than number arguments\")\n\t}\n\n\treturn buf.String(), newArgs, nil\n}\n\nfunc appendReflectSlice(args []interface{}, v reflect.Value, vlen int) []interface{} {\n\tswitch val := v.Interface().(type) {\n\tcase []interface{}:\n\t\targs = append(args, val...)\n\tcase []int:\n\t\tfor i := range val {\n\t\t\targs = append(args, val[i])\n\t\t}\n\tcase []string:\n\t\tfor i := range val {\n\t\t\targs = append(args, val[i])\n\t\t}\n\tdefault:","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/jmoiron/sqlx/blob/41dac167fdad5e3fd81d66cafba0951dc6823a30/bind.go#L222-L258","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\nquery, args, err := sqlx.In(\"SELECT * FROM users WHERE id IN (?)\", ids, status)\n// after\nquery, args, err := sqlx.In(\"SELECT * FROM users WHERE id IN (?) AND status=?\", ids, status)","handlingStrategy":"validation","validationCode":"placeholders := strings.Count(query, \"?\")\nif placeholders != len(args) {\n    return \"\", nil, fmt.Errorf(\"expected %d args, got %d\", placeholders, len(args))\n}\nq, a, err := sqlx.In(query, args...)","typeGuard":null,"tryCatchPattern":"query, args, err := sqlx.In(q, args...)\nif err != nil {\n    if strings.Contains(err.Error(), \"less than number arguments\") {\n        log.Printf(\"dropped args for query %s: got %d\", q, len(args))\n    }\n    return err\n}","preventionTips":["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"],"tags":["go","sqlx","query-building","bind-variables"],"backgroundTag":"bind-var-count-mismatch","analyzedSha":"41dac167fdad5e3fd81d66cafba0951dc6823a30","analyzedAt":"2026-09-03T06:10:20.382Z","contentChangedAt":"2026-09-03T06:10:20.382Z","schemaVersion":2},"datasetVersion":"2026-09-10T12:17:11.382Z"}