{"record":{"id":"e7406a2905487438","repo":"getsops/sops","slug":"failed-to-decrypt-sops-data-key-with-gcp-kms-key","errorCode":null,"errorMessage":"failed to decrypt sops data key with GCP KMS key: %w","messagePattern":"failed to decrypt sops data key with GCP KMS key: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gcpkms/keysource.go","lineNumber":255,"sourceCode":"\t\t}\n\t}()\n\n\t// NB: this is for compatibility with SOPS <=3.8.x. The previous GCP KMS\n\t// client used to work with base64 encoded strings.\n\tdecodedCipher, err := base64.StdEncoding.DecodeString(string(key.EncryptedDataKey()))\n\tif err != nil {\n\t\tlog.WithField(\"resourceID\", key.ResourceID).Info(\"Decryption failed\")\n\t\treturn nil, err\n\t}\n\n\treq := &kmspb.DecryptRequest{\n\t\tName:       key.ResourceID,\n\t\tCiphertext: decodedCipher,\n\t}\n\tresp, err := service.Decrypt(ctx, req)\n\tif err != nil {\n\t\tlog.WithField(\"resourceID\", key.ResourceID).Info(\"Decryption failed\")\n\t\treturn nil, fmt.Errorf(\"failed to decrypt sops data key with GCP KMS key: %w\", err)\n\t}\n\n\tlog.WithField(\"resourceID\", key.ResourceID).Info(\"Decryption succeeded\")\n\treturn resp.Plaintext, nil\n}\n\n// NeedsRotation returns whether the data key needs to be rotated or not.\nfunc (key *MasterKey) NeedsRotation() bool {\n\treturn time.Since(key.CreationDate) > (gcpkmsTTL)\n}\n\n// ToString converts the key to a string representation.\nfunc (key *MasterKey) ToString() string {\n\treturn key.ResourceID\n}\n\n// ToMap converts the MasterKey to a map for serialization purposes.\nfunc (key MasterKey) ToMap() map[string]interface{} {","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/getsops/sops/blob/13442bb98183887d7a9ac09ec8ab0564673a59d8/gcpkms/keysource.go#L237-L273","documentation":"The kms.Decrypt RPC failed inside DecryptContext after a successful client creation and base64 decoding of the stored ciphertext. Almost always an IAM/permission or key-state problem, since the ciphertext came from the encrypted SOPS file.","triggerScenarios":"service.Decrypt(ctx, req) errors during MasterKey.DecryptContext — identity lacks roles/cloudkms.cryptoKeyDecrypter, key disabled/destroyed, or ciphertext was produced by a different key than ResourceID points to.","commonSituations":"Rotated the key or changed project and the file still references the old key resource; decryption identity differs from encryption identity and lacks the decrypter role; running `sops -d` locally with a different gcloud account.","solutions":["Check the wrapped API error: grant roles/cloudkms.cryptoKeyDecrypter for 403.","Ensure the ResourceID in the .sops.yaml / file metadata still points to the existing, ENABLED key.","Run `gcloud auth list` / re-authenticate (`gcloud auth application-default login`) if using user credentials.","Re-encrypt the file with the correct key if the key was destroyed."],"exampleFix":"null","handlingStrategy":"try-catch","validationCode":"// Pre-check: gcloud kms keys describe KEY ... confirm ENABLED and that\n// the decrypting identity holds roles/cloudkms.cryptoKeyDecrypter.","typeGuard":"null","tryCatchPattern":"plaintext, err := key.DecryptContext(ctx)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to decrypt sops data key\") {\n        // check IAM decrypter role / key state / correct ResourceID\n    }\n    return err\n}","preventionTips":["Use the same identity (or a group with both encrypter+decrypter roles) for encrypt and decrypt.","When rotating keys, re-encrypt files before destroying old keys.","Verify ResourceID changes in .sops.yaml are intentional."],"tags":["gcp","kms","decryption","iam"],"backgroundTag":"kms-decrypt-denied","analyzedSha":"13442bb98183887d7a9ac09ec8ab0564673a59d8","analyzedAt":"2026-09-01T03:53:00.447Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}