{"record":{"id":"443ac60aaf671085","repo":"getsops/sops","slug":"no-decrypted-data","errorCode":null,"errorMessage":"no decrypted data","messagePattern":"no decrypted data","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hcvault/keysource.go","lineNumber":382,"sourceCode":"}\n\n// decryptPayload returns the payload for a decrypt request of the\n// encryptedKey.\nfunc decryptPayload(encryptedKey string) map[string]interface{} {\n\treturn map[string]interface{}{\n\t\t\"ciphertext\": encryptedKey,\n\t}\n}\n\n// dataKeyFromSecret attempts to extract the data key from the data of the\n// provided secret.\nfunc dataKeyFromSecret(secret *api.Secret) ([]byte, error) {\n\tif secret == nil || secret.Data == nil {\n\t\treturn nil, fmt.Errorf(\"transit backend is empty\")\n\t}\n\tdecrypted, ok := secret.Data[\"plaintext\"]\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"no decrypted data\")\n\t}\n\tplaintext, ok := decrypted.(string)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"decrypted plaintext data cannot be cast to string\")\n\t}\n\tdataKey, err := base64.StdEncoding.DecodeString(plaintext)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"cannot decode base64 plaintext into data key bytes\")\n\t}\n\treturn dataKey, nil\n}\n\n// vaultClient returns a new Vault client, configured with the given address\n// and token.\nfunc vaultClient(address, token string, hc *http.Client) (*api.Client, error) {\n\tcfg := api.DefaultConfig()\n\tcfg.Address = address\n","sourceCodeStart":364,"sourceCodeEnd":400,"githubUrl":"https://github.com/getsops/sops/blob/13442bb98183887d7a9ac09ec8ab0564673a59d8/hcvault/keysource.go#L364-L400","documentation":"This error is returned by dataKeyFromSecret when the secret and its Data map exist, but Data contains no \"plaintext\" key. The library throws it because a successful transit decrypt response must include base64 plaintext; without it the data key cannot be recovered in DecryptContext.","triggerScenarios":"Calling DecryptContext (or TestMasterKey_Encrypt) when the Vault transit decrypt response's Data lacks \"plaintext\" — e.g. ciphertext decrypted against the wrong key/mount, batch responses, or a policy-filtered response.","commonSituations":"Ciphertext created by a different Vault key or environment than the one decrypting; ACLs that strip the plaintext field; using a KV read instead of the transit decrypt endpoint; Vault-compatible servers with different response keys.","solutions":["Verify the decrypt uses the same transit mount and key that produced the ciphertext (key names are not interchangeable across mounts).","Test manually: `vault write transit/decrypt/<key> ciphertext=<blob>` and confirm the response contains data.plaintext.","Check the token's policy for update on transit/decrypt/<key> and that no response wrapping/filtering removes the field.","Ensure the encrypted blob format is a valid transit ciphertext (vault:v1:...) and not, e.g., an AWS/GCP ciphertext pasted by mistake."],"exampleFix":"// before: decrypting a blob made by another key\nkeyservice: hcvault://vault.example.com:8200/transit/keys/otherkey\n// after: use the original key that encrypted the data\nkeyservice: hcvault://vault.example.com:8200/transit/keys/mykey","handlingStrategy":"validation","validationCode":"// Verify the key exists and can decrypt before relying on it\nresp, err := client.Logical().Write(\"transit/decrypt/\"+keyName, map[string]interface{}{\"ciphertext\": blob})\nif err != nil { log.Fatal(err) }\nif resp == nil || resp.Data == nil {\n    log.Fatal(\"decrypt response empty; check key name and mount\")\n}\nif _, ok := resp.Data[\"plaintext\"]; !ok {\n    log.Fatal(\"decrypt response missing plaintext; ciphertext may be from another key\")\n}","typeGuard":"func hasPlaintext(secret *api.Secret) bool {\n    if secret == nil || secret.Data == nil { return false }\n    v, ok := secret.Data[\"plaintext\"]\n    if !ok { return false }\n    _, isStr := v.(string)\n    return isStr\n}","tryCatchPattern":null,"preventionTips":["Store the transit key name/mount in SOPS metadata and never change it after encrypting","Round-trip test (encrypt then decrypt) after any Vault migration","Check ACLs allow update on both transit/encrypt and transit/decrypt paths","Validate ciphertext blobs start with vault:v1: before decrypting"],"tags":["vault","transit","kms","go"],"backgroundTag":"vault-transit-plaintext-missing","analyzedSha":"13442bb98183887d7a9ac09ec8ab0564673a59d8","analyzedAt":"2026-09-01T03:53:00.447Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}