getsops/sops · error

Found duplicate %v entry

Error message

Found duplicate %v entry

What it means

Raised by ExtractMetadata when the sops tree contains more than one `sops` metadata entry in the first branch. The parser expects exactly one nested `sops` mapping holding the encrypted-file metadata; a second one is ambiguous and treated as corruption.

Source

Thrown at stores/metadata.go:116

	if err != nil {
		return md, err
	}
	err = d.Decode(m)
	return md, err
}

// 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]

View on GitHub (pinned to 13442bb981)

Solutions

  1. Open the file and remove the duplicate `sops` metadata block, keeping exactly one
  2. Restore the file from git (git checkout -- file) or from backup if a merge corrupted it
  3. Re-encrypt a known-good plaintext with sops instead of hand-repairing metadata
  4. Validate the file structure with a JSON/YAML parser to spot duplicated top-level keys

Example fix

// before (file contains two sops blocks after a bad merge)
{"data": "ENC[AES256_GCM,...]", "sops": {...}, "sops": {...}}
// after: keep exactly one sops metadata block
{"data": "ENC[AES256_GCM,...]", "sops": {"mac": "...", "pgp": [...]}}
Defensive patterns

Strategy: validation

Validate before calling

func countSopsKeys(b []byte) (int, error) {
	var m map[string]json.RawMessage
	if err := json.Unmarshal(b, &m); err != nil {
		return 0, err
	}
	return len(m["sops"]), nil // >1 means duplicate risk in raw text; check raw text for key duplication
}
// for raw text: count occurrences of the top-level sops key before loading
count := strings.Count(string(data), `"sops": {`)

Try / catch

tree, err := store.LoadEncryptedFile(data)
if err != nil {
	if strings.Contains(err.Error(), "Found duplicate sops entry") {
		return fmt.Errorf("file has more than one sops metadata block; resolve merge conflict or restore from git: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: Calling LoadEncryptedFile (which calls ExtractMetadata) on a file that literally contains two `sops:` metadata blocks — e.g. from a merge conflict, a concatenation of two encrypted files, a bad re-encryption, or a user manually editing the file and duplicating the sops block.

Common situations: Git merge conflicts in encrypted files resolved incorrectly, scripts that append sops output to existing files, YAML/JSON files edited by hand where the sops key was re-added, or file corruption from interrupted writes.

Related errors


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