googleapis/mcp-toolbox · error
unable to bind: %w
Error message
unable to bind: %w
What it means
ps.Bind(params.AsMap()) failed when converting the runtime-supplied parameter values to the prepared statement's bound form. This happens when the provided values don't match the declared parameter types/count (e.g. string where integer declared, missing or extra params) or a value is of a Go type the client cannot encode.
Source
Thrown at internal/sources/bigtable/bigtable.go:185
func (s *Source) RunSQL(ctx context.Context, statement string, configParam parameters.Parameters, params parameters.ParamValues) (any, error) {
mapParamsType, err := getMapParamsType(configParam)
if err != nil {
return nil, fmt.Errorf("fail to get map params: %w", err)
}
ps, err := s.BigtableClient().PrepareStatement(
ctx,
statement,
mapParamsType,
)
if err != nil {
return nil, fmt.Errorf("unable to prepare statement: %w", err)
}
bs, err := ps.Bind(params.AsMap())
if err != nil {
return nil, fmt.Errorf("unable to bind: %w", err)
}
out := []any{}
var rowErr error
err = bs.Execute(ctx, func(resultRow bigtable.ResultRow) bool {
vMap := make(map[string]any)
cols := resultRow.Metadata.Columns
for _, c := range cols {
var columValue any
if err = resultRow.GetByName(c.Name, &columValue); err != nil {
rowErr = err
return false
}
vMap[c.Name] = columValue
}
out = append(out, vMap)View on GitHub (pinned to 8cc6e09de2)
Solutions
- Check the wrapped error for which parameter failed to bind
- Ensure the caller supplies every declared parameter with the correct type (string vs integer vs array)
- Verify params order/naming matches the tool's declared parameters in tools.yaml
- Add explicit type coercion/validation at the tool level (e.g. declare type: integer and validate input before invoke)
Example fix
// before
{"user_id": "123"} // declared integer
// after
{"user_id": 123} // integer matching Int64SQLType Defensive patterns
Strategy: validation
Validate before calling
func validateParamValues(declared parameters.Parameters, values map[string]any) error {
for _, p := range declared {
v, ok := values[p.GetName()]
if !ok {
return fmt.Errorf("missing value for param %q", p.GetName())
}
switch p.GetType() {
case "integer":
if _, ok := v.(int64); !ok && !isIntLike(v) {
return fmt.Errorf("param %q must be integer, got %T", p.GetName(), v)
}
case "string":
if _, ok := v.(string); !ok {
return fmt.Errorf("param %q must be string, got %T", p.GetName(), v)
}
case "array":
if _, ok := v.([]any); !ok {
return fmt.Errorf("param %q must be array, got %T", p.GetName(), v)
}
}
}
return nil
} Type guard
func isIntLike(v any) bool {
switch v.(type) {
case int, int32, int64, float64:
return true
}
return false
} Try / catch
out, err := src.RunSQL(ctx, stmt, cfgParams, values)
if err != nil {
if strings.Contains(err.Error(), "unable to bind") {
// reject the request with a parameter type/count mismatch message
}
return err
} Prevention
- Enforce declared parameter types at the tool/API boundary before invoking RunSQL
- Send integers as JSON numbers (not quoted strings) when a param is declared integer
- Always supply every declared parameter; avoid optional params for Bigtable tools
- Coerce client input (strings from LLMs) to declared types before binding
When it happens
Trigger: RunSQL calls ps.Bind(params.AsMap()) with a params map whose keys or value types don't satisfy the prepared statement's declared SQLTypes (from getMapParamsType).
Common situations: Client (LLM) supplies a param value of the wrong type at invocation time; tool config expects a param the caller omitted; array param passed a non-array value; auth/instrumentation middleware injecting mismatched param sets.
Related errors
- unknow param type %s
- fail to get map params: %w
- invalid source for %q tool: source %q is not a compatible ty
- source is not compatible with the tool
- source is not compatible with the tool
AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05).
Data as JSON: /api/errors/bdba388460d4abd4.
Report an issue: GitHub.