getsops/sops · error
Found non-string key %q when flattening
Error message
Found non-string key %q when flattening
What it means
flattenDescendMap walks a sops.TreeBranch and needs each TreeItem key to be a string so it can join path segments. Keys that are neither sops.Comment nor string (numbers, bools, nested structs) cannot be joined into the flattened path, so flattening aborts with this error.
Source
Thrown at stores/flatten.go:235
Value: value,
})
(*destinationMap)[key] = true
return destination, nil
}
func flattenDescendMap(branch sops.TreeBranch, prefix string, destination sops.TreeBranch, destinationMap *map[string]bool) (sops.TreeBranch, error) {
for _, item := range branch {
if _, ok := item.Key.(sops.Comment); ok {
continue
}
if key, ok := item.Key.(string); ok {
var err error
destination, err = flattenDescendValue(item.Value, prefix+key, destination, destinationMap)
if err != nil {
return nil, err
}
} else {
return nil, fmt.Errorf("Found non-string key %q when flattening", item.Key)
}
}
return destination, nil
}
func flattenDescendArray(array []interface{}, prefix string, destination sops.TreeBranch, destinationMap *map[string]bool) (sops.TreeBranch, error) {
i := 0
for _, item := range array {
if _, ok := item.(sops.Comment); ok {
continue
}
var err error
destination, err = flattenDescendValue(item, fmt.Sprintf("%s%d", prefix, i), destination, destinationMap)
if err != nil {
return nil, err
}
i++
}View on GitHub (pinned to 13442bb981)
Solutions
- Convert all non-string keys to strings (fmt.Sprintf("%v", key)) before flattening
- Represent arrays as []interface{} values under a string key instead of numeric keys in the branch
- Use sops.Comment{} for comment entries so they are skipped rather than rejected
- Parse the document with the store matching its format so keys are normalized
Example fix
// before
sops.TreeBranch{{Key: 0, Value: "first"}}
// after
sops.TreeBranch{{Key: "items", Value: []interface{}{"first"}}} Defensive patterns
Strategy: type-guard
Validate before calling
for _, item := range branch {
if _, ok := item.Key.(sops.Comment); ok {
continue
}
if _, ok := item.Key.(string); !ok {
return fmt.Errorf("nested key %v is not a string; cannot flatten", item.Key)
}
} Type guard
func allStringKeys(branch sops.TreeBranch) bool {
for _, item := range branch {
if _, isComment := item.Key.(sops.Comment); isComment {
continue
}
if _, ok := item.Key.(string); !ok {
return false
}
}
return true
} Try / catch
flat, err := flattenTreeBranch(branch, "")
if err != nil {
if strings.Contains(err.Error(), "non-string key") {
return fmt.Errorf("normalize keys before flattening: %w", err)
}
return err
} Prevention
- Represent arrays as []interface{} values, not numeric-keyed entries
- Parse with the store matching the source format
- Use sops.Comment{} for comments so they are skipped
When it happens
Trigger: Calling flattenTreeBranch (directly or via stores.Flatten/MetadataFlattenBelowTop inside ExtractMetadata or ini EmitEncryptedFile's SerializeMetadata) on a branch where a nested item's Key is an int/bool/etc., e.g. sops.TreeItem{Key: 0, Value: ...} inside a subtree.
Common situations: Trees produced by other format stores that use numeric keys for arrays and were never converted; hand-assembled trees mixing types; passing a YAML/JSON-parsed branch straight into INI metadata serialization where array indices were left as ints.
Related errors
- Found non-string key %q when unflattening
- Error while unflattening: %w
- Found key collision %q while flattening
- Error encoding section: Section values should always be Tree
- invalid %s key configuration: expected string in list, got %
AI-assisted analysis of getsops/sops@13442bb981 (2026-09-01).
Data as JSON: /api/errors/3b55aa7c33187fe4.
Report an issue: GitHub.