getsops/sops · error

Found %v entry that is not a mapping

Error message

Found %v entry that is not a mapping

What it means

Raised by ExtractMetadata when a `sops` key is found in the first branch but its value is not a nested mapping (sops.TreeBranch). The metadata block must be an object/map; a scalar or array value under the `sops` key cannot hold the metadata fields.

Source

Thrown at stores/metadata.go:122

// ExtractMetadata extracts SOPS metadata from the supplied tree branches.
func ExtractMetadata(branches sops.TreeBranches, opts MetadataOpts) (sops.TreeBranches, sops.Metadata, error) {
	var metadataTree sops.TreeBranch
	if opts.Flatten != MetadataFlattenFull {
		first := true
		for bi, branch := range branches {
			i := 0
			for i < len(branch) {
				if branch[i].Key == SopsMetadataKey {
					if bi == 0 {
						if !first {
							return nil, sops.Metadata{}, fmt.Errorf("Found duplicate %v entry", SopsMetadataKey)
						}
						first = false
						if tree, ok := branch[i].Value.(sops.TreeBranch); ok {
							metadataTree = tree
						} else {
							return nil, sops.Metadata{}, fmt.Errorf("Found %v entry that is not a mapping", SopsMetadataKey)
						}
					}
					branch = append(branch[:i], branch[i+1:]...)
				} else {
					i++
				}
			}
			branches[bi] = branch
		}
	} else {
		if len(branches) >= 1 {
			branch := branches[0]
			for i := 0; i < len(branch); i++ {
				if key, ok := branch[i].Key.(string); ok {
					if strings.HasPrefix(key, SopsPrefix) {
						entry := branch[i]
						entry.Key = key[len(SopsPrefix):]
						metadataTree = append(metadataTree, entry)

View on GitHub (pinned to 13442bb981)

Solutions

  1. Rename the conflicting data key (anything other than `sops`) and re-encrypt
  2. If the file is meant to be encrypted, re-create it with `sops -e` so a proper sops metadata mapping is embedded
  3. If the file is plaintext, don't pass it to LoadEncryptedFile/decryption paths
  4. Restore the original metadata object if the file was edited by hand

Example fix

// before: key 'sops' used for data
{"sops": "ENC[AES256_GCM,...]"}
// after: rename data key; 'sops' is reserved for metadata
{"my_sops_config": "ENC[AES256_GCM,...]", "sops": {"mac": "...", ...}}
Defensive patterns

Strategy: validation

Validate before calling

var probe struct {
	Sops json.RawMessage `json:"sops"`
}
if err := json.Unmarshal(data, &probe); err == nil && len(probe.Sops) > 0 {
	var obj map[string]interface{}
	if err := json.Unmarshal(probe.Sops, &obj); err != nil || obj == nil {
		return errors.New(`"sops" key is present but not a JSON object`)
	}
}

Type guard

func isTreeBranch(v interface{}) bool {
	_, ok := v.(sops.TreeBranch)
	return ok
}

Try / catch

tree, err := store.LoadEncryptedFile(data)
if err != nil {
	if strings.Contains(err.Error(), "entry that is not a mapping") {
		return fmt.Errorf(`the "sops" key must hold a metadata object: %w`, err)
	}
	return err
}

Prevention

When it happens

Trigger: Calling LoadEncryptedFile on a file where the `sops` key maps to a non-object value — e.g. `sops: ENC[...]`, `sops: true`, `sops: [1,2]` — typically from a data key legitimately named `sops` holding encrypted data instead of metadata, or a hand-mangled file.

Common situations: A user's real data uses the key `sops` and they created the file without proper metadata (unencrypted copy that includes a data key literally named sops), manual edits that replaced the metadata object, or a format-mismatch where a plain data file is fed through an encrypted-file loader.

Related errors


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