{"record":{"id":"baedd7b154fedf11","repo":"getsops/sops","slug":"found-non-string-key-q-when-unflattening","errorCode":null,"errorMessage":"Found non-string key %q when unflattening","messagePattern":"Found non-string key %q when unflattening","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"stores/flatten.go","lineNumber":189,"sourceCode":"\t\tresult[k] = value\n\t}\n\treturn result, nil\n}\n\nfunc unflattenTreeBranch(branch sops.TreeBranch) (sops.TreeBranch, error) {\n\troot := &node{}\n\tfor _, item := range branch {\n\t\tif _, ok := item.Key.(sops.Comment); ok {\n\t\t\tcontinue\n\t\t}\n\t\tif key, ok := item.Key.(string); ok {\n\t\t\ttokens := tokenize(key)\n\t\t\terr := place(&item.Value, tokens, root)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"Error while unflattening %q: %w\", key, err)\n\t\t\t}\n\t\t} else {\n\t\t\treturn nil, fmt.Errorf(\"Found non-string key %q when unflattening\", item.Key)\n\t\t}\n\t}\n\tresult, err := convert(root)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"Error while unflattening: %w\", err)\n\t}\n\tif tb, ok := result.(sops.TreeBranch); ok {\n\t\treturn tb, nil\n\t}\n\treturn nil, fmt.Errorf(\"Internal error: cannot find root\")\n}\n\n////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n// Flatten\n\nfunc flattenDescendValue(value interface{}, key string, destination sops.TreeBranch, destinationMap *map[string]bool) (sops.TreeBranch, error) {\n\tswitch value := value.(type) {\n\tcase sops.TreeBranch:","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/getsops/sops/blob/13442bb98183887d7a9ac09ec8ab0564673a59d8/stores/flatten.go#L171-L207","documentation":"unflattenTreeBranch converts a flattened TreeBranch (dotted paths like \"a__map_b__list_0\") back into a nested sops.TreeBranch. It expects every item's Key to be a Go string produced by Flatten. When a key is neither a sops.Comment nor a string (e.g. int, bool, sops.Comment is skipped, but other types pass through), this error aborts the round-trip because tokenize() cannot build a path from a non-string key.","triggerScenarios":"Calling stores.ExtractMetadata (with Flatten enabled) on TreeBranches whose sops branch contains top-level TreeItem keys that are not strings and not sops.Comment values — for example a TreeItem{Key: 42, Value: ...} or a key produced programmatically as an int.","commonSituations":"Hand-built TreeBranches passed to sops instead of data loaded from a real store; custom store implementations that emit numeric keys (like JSON/YAML do for array indices) fed into the INI-related metadata extraction path; programmatic tree construction where keys were not normalized to strings via fmt.Sprintf.","solutions":["Convert every top-level key to a string before calling unflattenTreeBranch/ExtractMetadata (e.g. fmt.Sprintf(\"%v\", key))","Wrap numeric keys used for array positions in the expected flattened syntax (a__list_0) instead of using raw non-string keys","Skip or drop non-string items from the branch, converting them to sops.Comment if they are comments","Load the data through the store's LoadPlainFile/LoadEncryptedFile so keys are normalized to strings"],"exampleFix":"// before\nbranch := sops.TreeBranch{{Key: 1, Value: \"x\"}}\n_, err := stores.ExtractMetadata(sops.TreeBranches{branch}, opts)\n\n// after\nbranch := sops.TreeBranch{{Key: \"1\", Value: \"x\"}}\n_, err := stores.ExtractMetadata(sops.TreeBranches{branch}, opts)","handlingStrategy":"validation","validationCode":"for _, item := range branch {\n\tif _, ok := item.Key.(sops.Comment); ok {\n\t\tcontinue\n\t}\n\tif _, ok := item.Key.(string); !ok {\n\t\treturn fmt.Errorf(\"top-level key %v is not a string\", item.Key)\n\t}\n}","typeGuard":"func isStringKey(key interface{}) bool {\n\t_, ok := key.(string)\n\treturn ok\n}","tryCatchPattern":"tb, err := unflattenTreeBranch(branch)\nif err != nil {\n\tif strings.Contains(err.Error(), \"non-string key\") {\n\t\t// normalize keys to strings and retry\n\t}\n\treturn err\n}","preventionTips":["Always build TreeBranches with string keys","Convert numeric/other key types with fmt.Sprintf before use","Use the store Load* functions instead of hand-assembling trees"],"tags":["go","sops","type-mismatch","flatten"],"backgroundTag":"invalid-key-type","analyzedSha":"13442bb98183887d7a9ac09ec8ab0564673a59d8","analyzedAt":"2026-09-01T03:53:00.447Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}