getsops/sops · error
Error encoding section: Section values should always be Tree
Error message
Error encoding section: Section values should always be TreeBranches
What it means
In the INI store, every section's value must be a sops.TreeBranch (the list of key/value items inside that section). encodeTree type-asserts item.Value.(sops.TreeBranch); if the value is a scalar, array, or any other type, this error fires because INI has no way to represent a section whose body is not a key list.
Source
Thrown at stores/ini/store.go:42
func (store *Store) Name() string {
return "ini"
}
func (store Store) encodeTree(branches sops.TreeBranches) ([]byte, error) {
iniFile := ini.Empty(ini.LoadOptions{AllowNonUniqueSections: true})
iniFile.DeleteSection(ini.DefaultSection)
for _, branch := range branches {
for _, item := range branch {
if _, ok := item.Key.(sops.Comment); ok {
continue
}
section, err := iniFile.NewSection(item.Key.(string))
if err != nil {
return nil, fmt.Errorf("Error encoding section %s: %s", item.Key, err)
}
itemTree, ok := item.Value.(sops.TreeBranch)
if !ok {
return nil, fmt.Errorf("Error encoding section: Section values should always be TreeBranches")
}
first := 0
if len(itemTree) > 0 {
if sectionComment, ok := itemTree[0].Key.(sops.Comment); ok {
section.Comment = sectionComment.Value
first = 1
}
}
var lastItem *ini.Key
for i := first; i < len(itemTree); i++ {
keyVal := itemTree[i]
if comment, ok := keyVal.Key.(sops.Comment); ok {
if lastItem != nil {
lastItem.Comment = comment.Value
}
} else {View on GitHub (pinned to 13442bb981)
Solutions
- Wrap section values in sops.TreeBranch: move the scalar under a key, e.g. {Key: "settings", Value: sops.TreeBranch{{Key: "value", Value: "plain string"}}}
- Convert arrays to a TreeBranch of indexed keys ("0", "1", ...) or pick a list-capable store (json/yaml)
- Validate the tree shape (every top-level value is TreeBranch) before calling the INI store's emit functions
Example fix
// before
sops.TreeBranch{{Key: "settings", Value: "plain string"}}
// after
sops.TreeBranch{{Key: "settings", Value: sops.TreeBranch{{Key: "value", Value: "plain string"}}}} Defensive patterns
Strategy: type-guard
Validate before calling
for _, item := range branch {
if _, ok := item.Value.(sops.TreeBranch); !ok {
return fmt.Errorf("section %v value must be a TreeBranch, got %T", item.Key, item.Value)
}
} Type guard
func isTreeBranch(v interface{}) bool {
_, ok := v.(sops.TreeBranch)
return ok
} Try / catch
out, err := store.EmitPlainFile(branches)
if err != nil {
if strings.Contains(err.Error(), "should always be TreeBranches") {
return fmt.Errorf("restructure tree: wrap section values in sops.TreeBranch: %w", err)
}
return err
} Prevention
- Always nest section contents in sops.TreeBranch
- Validate tree shape before calling INI emit
- Don't reuse branches from other format stores without restructuring
When it happens
Trigger: Calling EmitEncryptedFile/EmitPlainFile with a top-level TreeItem whose value is not sops.TreeBranch — e.g. {Key: "settings", Value: "plain string"} or {Key: "arr", Value: []interface{}{...}}.
Common situations: Converting flat key=value data or JSON/YAML trees with scalar top-level values into INI; programmatic tree building that forgot to nest section keys under a TreeBranch; reusing branches decoded from a different store format without restructuring.
Related errors
- Found non-string key %q when unflattening
- Found non-string key %q when flattening
- Error encoding section %s: %s
- Error encoding key: %s
- Could not unmarshal input data: %s
AI-assisted analysis of getsops/sops@13442bb981 (2026-09-01).
Data as JSON: /api/errors/a553a09beb5c3bfa.
Report an issue: GitHub.