{"record":{"id":"e6a47c973858e9cc","repo":"getsops/sops","slug":"failed-to-decrypt-identity-file-v","errorCode":null,"errorMessage":"failed to decrypt identity file: %v","messagePattern":"failed to decrypt identity file: (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"age/encrypted_keys.go","lineNumber":73,"sourceCode":"\t\treturn fileKey, nil\n\t}\n\ti.NoMatchWarning()\n\treturn nil, age.ErrIncorrectIdentity\n}\n\nfunc (i *EncryptedIdentity) decrypt() error {\n\td, err := age.Decrypt(bytes.NewReader(i.Contents), &LazyScryptIdentity{i.Passphrase})\n\tif e := new(age.NoIdentityMatchError); errors.As(err, &e) {\n\t\t// ScryptIdentity returns ErrIncorrectIdentity for an incorrect\n\t\t// passphrase, which would lead Decrypt to returning \"no identity\n\t\t// matched any recipient\". That makes sense in the API, where there\n\t\t// might be multiple configured ScryptIdentity. Since in cmd/age there\n\t\t// can be only one, return a better error message.\n\t\ti.IncorrectPassphrase()\n\t\treturn fmt.Errorf(\"incorrect passphrase\")\n\t}\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to decrypt identity file: %v\", err)\n\t}\n\ti.identities, err = age.ParseIdentities(d)\n\treturn err\n}\n\n// LazyScryptIdentity is an age.Identity that requests a passphrase only if it\n// encounters an scrypt stanza. After obtaining a passphrase, it delegates to\n// ScryptIdentity.\ntype LazyScryptIdentity struct {\n\tPassphrase func() (string, error)\n}\n\nvar _ age.Identity = &LazyScryptIdentity{}\n\nfunc (i *LazyScryptIdentity) Unwrap(stanzas []*age.Stanza) (fileKey []byte, err error) {\n\tfor _, s := range stanzas {\n\t\tif s.Type == \"scrypt\" && len(stanzas) != 1 {\n\t\t\treturn nil, errors.New(\"an scrypt recipient must be the only one\")","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/getsops/sops/blob/13442bb98183887d7a9ac09ec8ab0564673a59d8/age/encrypted_keys.go#L55-L91","documentation":"While decrypting an encrypted age identity file, any read/decryption error other than the recognized incorrect-passphrase case is wrapped as \"failed to decrypt identity file\". This covers I/O errors and malformed ciphertext encountered before identities can be parsed. It signals the identity file could not be turned into plaintext at all.","triggerScenarios":"Calling Unwrap on a LazyScryptIdentity when reading/decrypting the encrypted identity file fails with an unexpected error — corrupted file, truncated download, unsupported armor, or I/O failure.","commonSituations":"Partially copied or corrupted key files; files encrypted with an incompatible age version; disk/permission errors mid-read.","solutions":["Verify the identity file is intact and is a valid age-encrypted (possibly armored) file.","Re-copy or restore the key file from a backup.","Update age/SOPS if the file was produced by a newer format version."],"exampleFix":"// before\n$ head -c 100 key.txt > truncated-key.txt  # corrupted key file\n// after\n$ age -d -o key.txt key.txt.age            # restore full, valid identity file","handlingStrategy":"validation","validationCode":"data, err := os.ReadFile(keyPath)\nif err != nil {\n    return err\n}\nif len(data) == 0 || !bytes.HasPrefix(bytes.TrimSpace(data), []byte(\"-----BEGIN\")) && !isAgeFile(data) {\n    return fmt.Errorf(\"%s does not look like an age identity file\", keyPath)\n}","typeGuard":null,"tryCatchPattern":"fileKey, err := identity.Unwrap(stanzas)\nif err != nil && strings.Contains(err.Error(), \"failed to decrypt identity file\") {\n    // restore the key file from backup before retrying\n}","preventionTips":["Checksum key files after download/copy and compare before use","Store encrypted key files with proper file integrity controls","Keep backups of age identities"],"tags":["age","identity","decryption","corrupt-file","keyfile"],"backgroundTag":"identity-file-decrypt-failed","analyzedSha":"13442bb98183887d7a9ac09ec8ab0564673a59d8","analyzedAt":"2026-09-01T03:53:00.447Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}