googleapis/mcp-toolbox · error

can't convert sorts to array of strings

Error message

can't convert sorts to array of strings: %s

What it means

ProcessQueryArgs converts the raw 'sorts' parameter (declared as []any) into []string with parameters.ConvertAnySliceToTyped. Non-string elements (numbers, booleans, objects, nil) cause the conversion to fail and this wrapped error is returned before the query request is built.

Solutions

  1. Send sorts as an array of Looker sort strings of the form "<field_name> <asc|desc>", e.g. ["users.count desc"]
  2. Verify the parameter is an array, not a single string or object
  3. Convert programmatic values to strings before calling
  4. Inspect the upstream error in the message for the exact offending element

Example fix

// before
"sorts": [{"field": "users.count", "dir": "desc"}]
// after
"sorts": ["users.count desc"]
Defensive patterns

Strategy: validation

Validate before calling

func validSorts(v any) bool {
	s, ok := v.([]any)
	if !ok { return false }
	for _, e := range s {
		str, ok := e.(string)
		if !ok { return false }
		parts := strings.Fields(str)
		if len(parts) == 0 || len(parts) > 2 { return false }
	}
	return true
}

Type guard

if s, ok := params["sorts"].([]any); ok && allStrings(s) { /* safe */ }

Prevention

When it happens

Trigger: Invoking a Looker query tool with sorts containing non-string entries, e.g. sorts: [1] or sorts: [{"field":"x","dir":"asc"}] instead of ["users.count desc"].

Common situations: Clients passing structured sort objects rather than Looker's 'field direction' strings; agents generating numeric values; copy-pasted configs from other APIs with different sort formats.

Related errors


AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05). Data as JSON: /api/errors/6bf44e515ef805fd. Report an issue: GitHub.

Appendix: source

Thrown at internal/tools/looker/lookercommon/lookercommon.go:333

		if len(k) >= 2 && (k[0] == '\'' || k[0] == '"') && k[0] == k[len(k)-1] {
			newKey = k[1 : len(k)-1]
		}
		newVal := v
		if s, ok := v.(string); ok && len(s) >= 2 &&
			(s[0] == '\'' || s[0] == '"') && s[0] == s[len(s)-1] {
			newVal = s[1 : len(s)-1]
		}
		processedFilters[newKey] = newVal
	}
	filters = processedFilters
	p, err := parameters.ConvertAnySliceToTyped(paramsMap["pivots"].([]any), "string")
	if err != nil {
		return nil, fmt.Errorf("can't convert pivots to array of strings: %s", err)
	}
	pivots := p.([]string)
	s, err := parameters.ConvertAnySliceToTyped(paramsMap["sorts"].([]any), "string")
	if err != nil {
		return nil, fmt.Errorf("can't convert sorts to array of strings: %s", err)
	}
	sorts := s.([]string)
	limit := fmt.Sprintf("%v", paramsMap["limit"].(int))

	var tz string
	if paramsMap["tz"] != nil {
		tz = paramsMap["tz"].(string)
	} else {
		tzname, err := tzlocal.RuntimeTZ()
		if err != nil {
			logger.ErrorContext(ctx, fmt.Sprintf("Error getting local timezone: %s", err))
			tzname = "Etc/UTC"
		}
		tz = tzname
	}

	var filterExpressionPtr *string
	if val, ok := paramsMap["filter_expression"]; ok && val != nil {

View on GitHub (pinned to 8cc6e09de2)