{"record":{"id":"80f43399c927d7d4","repo":"getsops/sops","slug":"unexpected-key-type-t","errorCode":null,"errorMessage":"Unexpected key type %T","messagePattern":"Unexpected key type %T","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"stores/metadata.go","lineNumber":50,"sourceCode":"\t// Only used if Flatten is not MetadataFlattenNone.\n\t// This does provide a double escape for newlines, since the store itself\n\t// is already expected to take care of them. This is mainly needed for\n\t// backwards compatibility with the INI store.\n\tEscapeNewlines bool\n}\n\n// SopsPrefix is the prefix for all metadata entry keys.\nconst SopsPrefix = SopsMetadataKey + \"_\"\n\nfunc sopsToGoMap(mapping sops.TreeBranch) (map[string]interface{}, error) {\n\tresult := make(map[string]interface{})\n\tfor _, item := range mapping {\n\t\tif _, ok := item.Key.(sops.Comment); ok {\n\t\t\tcontinue\n\t\t}\n\t\tkey, ok := item.Key.(string)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"Unexpected key type %T\", item.Key)\n\t\t}\n\t\tvalue, err := sopsToGo(item.Value)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tresult[key] = value\n\t}\n\treturn result, nil\n}\n\nfunc sopsToGoSlice(slice []interface{}) ([]interface{}, error) {\n\tresult := make([]interface{}, 0, len(slice))\n\tfor _, item := range slice {\n\t\tif _, ok := item.(sops.Comment); ok {\n\t\t\tcontinue\n\t\t}\n\t\tvalue, err := sopsToGo(item)\n\t\tif err != nil {","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/getsops/sops/blob/13442bb98183887d7a9ac09ec8ab0564673a59d8/stores/metadata.go#L32-L68","documentation":"Raised inside sopsToGoMap while converting a sops tree branch into plain Go maps: a tree item key is neither a sops.Comment nor a string. The JSON/YAML stores only produce string keys, so this signals a tree built or mutated with a non-string key type.","triggerScenarios":"Calling sopsToGo (directly or via treeBranchToMetadata during ExtractMetadata) on a branch containing a TreeItem whose Key is, e.g., an int, bool, or custom type instead of string.","commonSituations":"Custom stores or plugins that insert numeric/typed keys into branches, programmatic tree manipulation (e.g. appending items with int keys), or code ported from other formats where keys aren't strings.","solutions":["Convert all TreeItem keys to strings before conversion: fmt.Sprint(key) or string coercion","Skip/strip non-string key items from the branch before conversion","Fix the upstream code that inserts non-string keys into the tree"],"exampleFix":"// before\nbranch = append(branch, sops.TreeItem{Key: 42, Value: v})\n// after\nbranch = append(branch, sops.TreeItem{Key: strconv.Itoa(42), Value: v})","handlingStrategy":"type-guard","validationCode":"func allStringKeys(branch sops.TreeBranch) bool {\n\tfor _, item := range branch {\n\t\tif _, ok := item.Key.(string); !ok {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}","typeGuard":"func isStringKey(k interface{}) bool {\n\t_, ok := k.(string)\n\treturn ok\n}","tryCatchPattern":"result, err := sopsToGo(branch)\nif err != nil {\n\tif strings.HasPrefix(err.Error(), \"Unexpected key type\") {\n\t\treturn fmt.Errorf(\"tree built with non-string key: %w\", err)\n\t}\n\treturn err\n}","preventionTips":["Only build tree branches with string keys","Coerce non-string keys with fmt.Sprint before inserting","Audit custom store/plugin code that appends TreeItems"],"tags":["tree","keys","type-conversion","internal"],"backgroundTag":"unexpected-key-type","analyzedSha":"13442bb98183887d7a9ac09ec8ab0564673a59d8","analyzedAt":"2026-09-01T03:53:00.447Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}