googleapis/mcp-toolbox · error

cannot unmarshal %T into StringOrStringSlice

Error message

cannot unmarshal %T into StringOrStringSlice

What it means

This error comes from the custom UnmarshalYAML implementation of the StringOrStringSlice type in the BigQuery source config. StringOrStringSlice accepts either a single YAML string or a sequence of strings; if the YAML value is neither (e.g. a number, boolean, map, or a sequence containing non-strings), the unmarshaller returns this error. It is thrown at config-parse time before any connection is made.

Source

Thrown at internal/sources/bigquery/bigquery.go:131

	var v any
	if err := unmarshal(&v); err != nil {
		return err
	}
	switch val := v.(type) {
	case string:
		*s = strings.Split(val, ",")
		return nil
	case []any:
		for _, item := range val {
			if str, ok := item.(string); ok {
				*s = append(*s, str)
			} else {
				return fmt.Errorf("element in sequence is not a string: %v", item)
			}
		}
		return nil
	}
	return fmt.Errorf("cannot unmarshal %T into StringOrStringSlice", v)
}

func (r Config) SourceConfigType() string {
	// Returns BigQuery source type
	return SourceType
}

func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) {
	if r.WriteMode == "" {
		r.WriteMode = WriteModeAllowed
		if r.ReadOnly != nil && *r.ReadOnly {
			r.WriteMode = WriteModeBlocked
		}
	}

	if r.WriteMode != WriteModeAllowed && r.WriteMode != WriteModeBlocked && r.WriteMode != WriteModeProtected {
		return nil, fmt.Errorf("invalid writeMode %q: must be one of %q, %q, or %q", r.WriteMode, WriteModeAllowed, WriteModeProtected, WriteModeBlocked)
	}

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Check the YAML field: it must be a single string or a list of strings.
  2. Quote every element in the list so YAML does not parse it as a number or boolean (e.g. ["123", "true"]).
  3. If you need a single value, write it as a plain string instead of a nested list/map.

Example fix

// before (invalid: non-string element)
allowedDatasets:
  - 12345
  - mydataset
// after
allowedDatasets:
  - "12345"
  - mydataset
Defensive patterns

Strategy: validation

Validate before calling

func validateStringOrStringSlice(v interface{}) error {
	normalize := func(s interface{}) error {
		_, ok := s.(string)
		if !ok { return fmt.Errorf("not a string: %v", s) }
		return nil
	}
	switch t := v.(type) {
	case string, nil:
		return nil
	case []interface{}:
		for _, item := range t {
			if err := normalize(item); err != nil { return err }
		}
		return nil
	default:
		return fmt.Errorf("cannot unmarshal %T into StringOrStringSlice", v)
	}
}

Type guard

func isStringOrStringSlice(v interface{}) bool {
	switch t := v.(type) {
	case string:
		return true
	case []interface{}:
		for _, item := range t {
			if _, ok := item.(string); !ok { return false }
		}
		return true
	default:
		return false
	}
}

Try / catch

var cfg Config
if err := yaml.Unmarshal(data, &cfg); err != nil {
	if strings.Contains(err.Error(), "StringOrStringSlice") {
		log.Fatalf("config field must be a string or list of strings: %v", err)
	}
	return err
}

Prevention

When it happens

Trigger: Parsing a BigQuery source YAML config where a field of type StringOrStringSlice (e.g. allowedDatasets) is given a YAML scalar of non-string type (integer, bool), a nested mapping, or a list whose elements are not all strings.

Common situations: Writing `allowedDatasets: 123` or `allowedDatasets: [mydataset, 42]` in tools.yaml; quoting mistakes that turn a list into a map; typos producing a nested structure where a string list was expected.

Related errors


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