{"record":{"id":"6bf44e515ef805fd","repo":"googleapis/mcp-toolbox","slug":"can-t-convert-sorts-to-array-of-strings-s","errorCode":null,"errorMessage":"can't convert sorts to array of strings: %s","messagePattern":"can't convert sorts to array of strings: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/tools/looker/lookercommon/lookercommon.go","lineNumber":333,"sourceCode":"\t\tif len(k) >= 2 && (k[0] == '\\'' || k[0] == '\"') && k[0] == k[len(k)-1] {\n\t\t\tnewKey = k[1 : len(k)-1]\n\t\t}\n\t\tnewVal := v\n\t\tif s, ok := v.(string); ok && len(s) >= 2 &&\n\t\t\t(s[0] == '\\'' || s[0] == '\"') && s[0] == s[len(s)-1] {\n\t\t\tnewVal = s[1 : len(s)-1]\n\t\t}\n\t\tprocessedFilters[newKey] = newVal\n\t}\n\tfilters = processedFilters\n\tp, err := parameters.ConvertAnySliceToTyped(paramsMap[\"pivots\"].([]any), \"string\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't convert pivots to array of strings: %s\", err)\n\t}\n\tpivots := p.([]string)\n\ts, err := parameters.ConvertAnySliceToTyped(paramsMap[\"sorts\"].([]any), \"string\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't convert sorts to array of strings: %s\", err)\n\t}\n\tsorts := s.([]string)\n\tlimit := fmt.Sprintf(\"%v\", paramsMap[\"limit\"].(int))\n\n\tvar tz string\n\tif paramsMap[\"tz\"] != nil {\n\t\ttz = paramsMap[\"tz\"].(string)\n\t} else {\n\t\ttzname, err := tzlocal.RuntimeTZ()\n\t\tif err != nil {\n\t\t\tlogger.ErrorContext(ctx, fmt.Sprintf(\"Error getting local timezone: %s\", err))\n\t\t\ttzname = \"Etc/UTC\"\n\t\t}\n\t\ttz = tzname\n\t}\n\n\tvar filterExpressionPtr *string\n\tif val, ok := paramsMap[\"filter_expression\"]; ok && val != nil {","sourceCodeStart":315,"sourceCodeEnd":351,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/internal/tools/looker/lookercommon/lookercommon.go#L315-L351","documentation":"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.","triggerScenarios":"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\"].","commonSituations":"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.","solutions":["Send sorts as an array of Looker sort strings of the form \"<field_name> <asc|desc>\", e.g. [\"users.count desc\"]","Verify the parameter is an array, not a single string or object","Convert programmatic values to strings before calling","Inspect the upstream error in the message for the exact offending element"],"exampleFix":"// before\n\"sorts\": [{\"field\": \"users.count\", \"dir\": \"desc\"}]\n// after\n\"sorts\": [\"users.count desc\"]","handlingStrategy":"validation","validationCode":"func validSorts(v any) bool {\n\ts, ok := v.([]any)\n\tif !ok { return false }\n\tfor _, e := range s {\n\t\tstr, ok := e.(string)\n\t\tif !ok { return false }\n\t\tparts := strings.Fields(str)\n\t\tif len(parts) == 0 || len(parts) > 2 { return false }\n\t}\n\treturn true\n}","typeGuard":"if s, ok := params[\"sorts\"].([]any); ok && allStrings(s) { /* safe */ }","tryCatchPattern":null,"preventionTips":["Use Looker 'field direction' strings for sorts","Convert structured sort objects to strings before invoking","Keep numeric/boolean values out of array-of-string parameters"],"tags":["looker","type-conversion","parameters"],"backgroundTag":"parameter-type-mismatch","analyzedSha":"8cc6e09de2ad7b8bffc77751799585a1401a48eb","analyzedAt":"2026-09-05T01:10:36.887Z","contentChangedAt":"2026-09-05T01:10:36.887Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}