{"record":{"id":"57ec6e8dca922ce2","repo":"googleapis/mcp-toolbox","slug":"unexpected-non-string-key-for-entry-in-s-v","errorCode":null,"errorMessage":"unexpected non-string key for entry in '%s': %v","messagePattern":"unexpected non-string key for entry in '(.+?)': (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/internal/config.go","lineNumber":359,"sourceCode":"\t\t\tname, _ = input[nameIndex].Value.(string)\n\t\t}\n\t\t// Warning is best effort: a caller without a logger in context still\n\t\t// gets the conversion.\n\t\tif logger, err := util.LoggerFromContext(ctx); err == nil {\n\t\t\tlogger.WarnContext(ctx, fmt.Sprintf(\"toolset %q: dropping description, which a toolset does not support; declare the collection as `kind: group` to keep it\", name))\n\t\t}\n\t}\n\treturn migrated\n}\n\n// transformDocs transforms the configuration file from nested to flat format\n// yaml.MapSlice will preserve the order in a map\nfunc transformDocs(kind string, input yaml.MapSlice) ([]yaml.MapSlice, error) {\n\tvar transformed []yaml.MapSlice\n\tfor _, entry := range input {\n\t\tentryName, ok := entry.Key.(string)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"unexpected non-string key for entry in '%s': %v\", kind, entry.Key)\n\t\t}\n\t\tentryBody := processValue(entry.Value, kind == \"toolset\")\n\n\t\tcurrentTransformed := yaml.MapSlice{\n\t\t\t{Key: \"kind\", Value: kind},\n\t\t\t{Key: \"name\", Value: entryName},\n\t\t}\n\n\t\t// Merge the transformed body into our result\n\t\tif bodySlice, ok := entryBody.(yaml.MapSlice); ok {\n\t\t\tcurrentTransformed = append(currentTransformed, bodySlice...)\n\t\t} else {\n\t\t\treturn nil, fmt.Errorf(\"unable to convert entryBody to MapSlice\")\n\t\t}\n\t\ttransformed = append(transformed, currentTransformed)\n\t}\n\treturn transformed, nil\n}","sourceCodeStart":341,"sourceCodeEnd":377,"githubUrl":"https://github.com/googleapis/mcp-toolbox/blob/8cc6e09de2ad7b8bffc77751799585a1401a48eb/cmd/internal/config.go#L341-L377","documentation":"transformDocs converts each top-level YAML entry (source, toolset, etc.) into a per-entry document slice. Every YAML map key must be a string because each key becomes the resource 'name'. This error is thrown when a key in a 'kind' section is not a string (e.g. a number or boolean key).","triggerScenarios":"A key under sources/tools/toolsets/authServices/prompts/groups in the toolbox YAML is a non-string scalar, e.g. `12345: ...` or `true: ...`, which yaml.MapSlice parses as a non-string type.","commonSituations":"Hand-edited YAML where a source name was written as a number (e.g. a port or an ID used directly as a key); copy-pasted JSON with numeric keys; YAML anchors or unquoted keys that parse as bool/int (e.g. `on:`, `yes:` which YAML 1.1 treats as booleans).","solutions":["Quote the key so it parses as a string: `\"12345\": ...` instead of `12345: ...`","Rename the entry key to a valid identifier string","Check for YAML 1.1 boolean-like keys (on/off/yes/no) and quote them"],"exampleFix":"// before\nsources:\n  8080:\n    kind: postgres\n// after\nsources:\n  \"8080\":\n    kind: postgres","handlingStrategy":"validation","validationCode":"func validateStringKeys(input yaml.MapSlice) error {\n  for _, e := range input {\n    if _, ok := e.Key.(string); !ok {\n      return fmt.Errorf(\"non-string key: %v\", e.Key)\n    }\n  }\n  return nil\n}","typeGuard":"func isStringKey(k interface{}) bool { _, ok := k.(string); return ok }","tryCatchPattern":"cfg, err := parser.ConvertConfig(ctx, data)\nif err != nil {\n  if strings.Contains(err.Error(), \"non-string key\") {\n    // fix YAML keys, re-validate\n  }\n  return err\n}","preventionTips":["Quote all YAML map keys, especially numeric or boolean-like ones (on, yes, 123)","Run a YAML linter on config files before deploying","Pin an editor schema for toolbox configs"],"tags":["yaml","config-parsing","type-mismatch"],"backgroundTag":"invalid-yaml-key","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"}