getsops/sops · error
no decrypted data
Error message
no decrypted data
What it means
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.
Source
Thrown at hcvault/keysource.go:382
}
// decryptPayload returns the payload for a decrypt request of the
// encryptedKey.
func decryptPayload(encryptedKey string) map[string]interface{} {
return map[string]interface{}{
"ciphertext": encryptedKey,
}
}
// dataKeyFromSecret attempts to extract the data key from the data of the
// provided secret.
func dataKeyFromSecret(secret *api.Secret) ([]byte, error) {
if secret == nil || secret.Data == nil {
return nil, fmt.Errorf("transit backend is empty")
}
decrypted, ok := secret.Data["plaintext"]
if !ok {
return nil, fmt.Errorf("no decrypted data")
}
plaintext, ok := decrypted.(string)
if !ok {
return nil, fmt.Errorf("decrypted plaintext data cannot be cast to string")
}
dataKey, err := base64.StdEncoding.DecodeString(plaintext)
if err != nil {
return nil, fmt.Errorf("cannot decode base64 plaintext into data key bytes")
}
return dataKey, nil
}
// vaultClient returns a new Vault client, configured with the given address
// and token.
func vaultClient(address, token string, hc *http.Client) (*api.Client, error) {
cfg := api.DefaultConfig()
cfg.Address = address
View on GitHub (pinned to 13442bb981)
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.
Example fix
// before: decrypting a blob made by another key keyservice: hcvault://vault.example.com:8200/transit/keys/otherkey // after: use the original key that encrypted the data keyservice: hcvault://vault.example.com:8200/transit/keys/mykey
Defensive patterns
Strategy: validation
Validate before calling
// Verify the key exists and can decrypt before relying on it
resp, err := client.Logical().Write("transit/decrypt/"+keyName, map[string]interface{}{"ciphertext": blob})
if err != nil { log.Fatal(err) }
if resp == nil || resp.Data == nil {
log.Fatal("decrypt response empty; check key name and mount")
}
if _, ok := resp.Data["plaintext"]; !ok {
log.Fatal("decrypt response missing plaintext; ciphertext may be from another key")
} Type guard
func hasPlaintext(secret *api.Secret) bool {
if secret == nil || secret.Data == nil { return false }
v, ok := secret.Data["plaintext"]
if !ok { return false }
_, isStr := v.(string)
return isStr
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- no encrypted data
- encrypted ciphertext cannot be cast to string
- decrypted plaintext data cannot be cast to string
- failed to encrypt sops data key to Vault transit backend '%s
- failed to decrypt sops data key from Vault transit backend '
AI-assisted analysis of getsops/sops@13442bb981 (2026-09-01).
Data as JSON: /api/errors/443ac60aaf671085.
Report an issue: GitHub.