googleapis/mcp-toolbox · error

'explore' must be a string, got %T

Error message

'explore' must be a string, got %T

What it means

Runtime type check in ProcessFieldArgs: the invocation's `explore` parameter is not a string (the message reports the actual Go type received, e.g. map or float64), so the target Looker explore cannot be identified.

Source

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

		filtersParameter,
		pivotsParameter,
		sortsParameter,
		limitParameter,
		tzParameter,
		filterExpressionParameter,
		dynamicFieldsParameter,
	}
}

func ProcessFieldArgs(ctx context.Context, params parameters.ParamValues) (*string, *string, error) {
	mapParams := params.AsMap()
	model, ok := mapParams["model"].(string)
	if !ok {
		return nil, nil, fmt.Errorf("'model' must be a string, got %T", mapParams["model"])
	}
	explore, ok := mapParams["explore"].(string)
	if !ok {
		return nil, nil, fmt.Errorf("'explore' must be a string, got %T", mapParams["explore"])
	}
	return &model, &explore, nil
}

// escapeUnquotedParameterValue escapes Looker filter-expression metacharacters
// so the value reaches a type: unquoted parameter without being interpreted as
// a wildcard pattern. Looker treats `_` as a single-character wildcard and `%`
// as a multi-character wildcard, and rejects either inside unquoted-parameter
// values; `,` is the filter-expression value separator; `^` is the escape
// character itself. Already-escaped sequences (`^_`, `^%`, `^,`, `^^`) pass
// through unchanged, which keeps the function idempotent for callers that pass
// pre-escaped forms (e.g. round-tripping `default_filter_value` from the
// explore metadata). Lone metacharacters get a `^` prefix; lone `^` is doubled
// to `^^`. Walking rune-by-rune (not whole-string scanning) means a value with
// both an already-escaped sequence and an unescaped metacharacter gets each
// half handled correctly — e.g. `first^_touch_v2` becomes `first^_touch^_v2`.
func escapeUnquotedParameterValue(value string) string {
	var sb strings.Builder

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Send 'explore' as a JSON string, e.g. {"explore": "order_items"}.
  2. Ensure both 'model' and 'explore' are present in the tool call.
  3. Validate the request payload types client-side before invoking.

Example fix

// before
{"model": "ecommerce", "explore": true}
// after
{"model": "ecommerce", "explore": "order_items"}
Defensive patterns

Strategy: validation

Validate before calling

// Client-side check before invoking
const explore = "order_items";
if (typeof explore !== "string" || explore.length === 0) {
    throw new Error("'explore' must be a non-empty string");
}

Type guard

e, ok := mapParams["explore"]
explore, isStr := e.(string)
if !ok || !isStr {
    return fmt.Errorf("explore must be string, got %T", e)
}

Try / catch

model, explore, err := lookercommon.ProcessFieldArgs(ctx, params)
if err != nil {
    return nil, util.NewInvalidArgumentError("check 'explore' parameter: %v", err)
}

Prevention

When it happens

Trigger: Invoke receives params where mapParams["explore"] is nil (missing) or a non-string dynamic value (number, bool, array).

Common situations: Client omitting 'explore'; JSON payload typing explore as a number; programmatic invocation passing the wrong type; template interpolation injecting a non-string.

Related errors


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