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

  1. Read the wrapped inner error (Duplicate value / Type mismatch / Incomplete list) and fix the offending key(s) it points to
  2. Ensure lists use contiguous 0-based indices with no gaps
  3. Do not set both a leaf value and child keys at the same path — pick one
  4. 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

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


AI-assisted analysis of getsops/sops@13442bb981 (2026-09-01). Data as JSON: /api/errors/af83cbaf65d0c124. Report an issue: GitHub.