getsops/sops · error
Error while unflattening: %w
Error message
Error while unflattening: %w
What it means
After tokenizing and placing all flattened keys into a node tree, unflattenTreeBranch calls convert(root) to rebuild the nested structure. convert fails with "Duplicate value", "Type mismatch", or "Incomplete list", and this wrapper re-raises it. It means the flattened key set does not correspond to a single consistent nested tree.
Source
Thrown at stores/flatten.go:194
func unflattenTreeBranch(branch sops.TreeBranch) (sops.TreeBranch, error) {
root := &node{}
for _, item := range branch {
if _, ok := item.Key.(sops.Comment); ok {
continue
}
if key, ok := item.Key.(string); ok {
tokens := tokenize(key)
err := place(&item.Value, tokens, root)
if err != nil {
return nil, fmt.Errorf("Error while unflattening %q: %w", key, err)
}
} else {
return nil, fmt.Errorf("Found non-string key %q when unflattening", item.Key)
}
}
result, err := convert(root)
if err != nil {
return nil, fmt.Errorf("Error while unflattening: %w", err)
}
if tb, ok := result.(sops.TreeBranch); ok {
return tb, nil
}
return nil, fmt.Errorf("Internal error: cannot find root")
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Flatten
func flattenDescendValue(value interface{}, key string, destination sops.TreeBranch, destinationMap *map[string]bool) (sops.TreeBranch, error) {
switch value := value.(type) {
case sops.TreeBranch:
return flattenDescendMap(value, key+mapSeparator, destination, destinationMap)
case []interface{}:
return flattenDescendArray(value, key+listSeparator, destination, destinationMap)
}
if _, ok := (*destinationMap)[key]; ok {View on GitHub (pinned to 13442bb981)
Solutions
- Read the wrapped inner error (Duplicate value / Type mismatch / Incomplete list) and fix the offending key(s) it points to
- Ensure lists use contiguous 0-based indices with no gaps
- Do not set both a leaf value and child keys at the same path — pick one
- Regenerate the flattened keys with stores Flatten rather than hand-crafting them
Example fix
// before: keys "a__list_0" and "a__list_2" exist (gap -> Incomplete list) // after: keys "a__list_0", "a__list_1", "a__list_2" (contiguous)
Defensive patterns
Strategy: validation
Validate before calling
seen := map[string]bool{}
for _, item := range branch {
if seen[item.Key.(string)] {
return fmt.Errorf("duplicate flattened key %q", item.Key)
}
seen[item.Key.(string)] = true
} Try / catch
tb, err := unflattenTreeBranch(branch)
if err != nil {
var inner string
if errors.Unwrap(err) != nil {
inner = errors.Unwrap(err).Error()
}
log.Printf("unflatten failed: %v (inner: %s)", err, inner)
return err
} Prevention
- Generate flattened keys with the Flatten functions, never by hand
- Keep list indices contiguous starting at 0
- Never place both a leaf value and children at the same path
- Validate after merges/diffs before decrypting
When it happens
Trigger: Calling ExtractMetadata/unflattenTreeBranch with branches whose flattened keys collide semantically: e.g. both "a" and "a__map_b" set as leaf values (value + subkey = Type mismatch), the same leaf path supplied twice (Duplicate value), or a list with missing indices like __list_0 and __list_2 without __list_1 (Incomplete list).
Common situations: A manually edited encrypted file where the sops metadata section was mangled; a diff/merge that dropped one element of a list so indices are no longer contiguous; a script that wrote keys with inconsistent separator usage (__map_ vs plain dot); partial round-trip through another format that lost keys.
Related errors
- Found non-string key %q when unflattening
- Found key collision %q while flattening
- Found non-string key %q when flattening
- Failed to read %q: %w
- Internal error: cannot find root
AI-assisted analysis of getsops/sops@13442bb981 (2026-09-01).
Data as JSON: /api/errors/af83cbaf65d0c124.
Report an issue: GitHub.