getsops/sops · error

no encrypted data

Error message

no encrypted data

What it means

This error comes from encryptedKeyFromSecret in the HashiCorp Vault transit keysource helper. It means Vault returned a valid secret whose Data map exists, but the map contains no "ciphertext" key. The library throws it because without a ciphertext entry it cannot wrap the data-encryption key during EncryptContext.

Source

Thrown at hcvault/keysource.go:357

}

// encryptPayload returns the payload for an encrypt request of the dataKey.
func encryptPayload(dataKey []byte) map[string]interface{} {
	encoded := base64.StdEncoding.EncodeToString(dataKey)
	return map[string]interface{}{
		"plaintext": encoded,
	}
}

// encryptedKeyFromSecret attempts to extract the encrypted key from the data
// of the provided secret.
func encryptedKeyFromSecret(secret *api.Secret) (string, error) {
	if secret == nil || secret.Data == nil {
		return "", fmt.Errorf("transit backend is empty")
	}
	encrypted, ok := secret.Data["ciphertext"]
	if !ok {
		return "", fmt.Errorf("no encrypted data")
	}
	encryptedKey, ok := encrypted.(string)
	if !ok {
		return "", fmt.Errorf("encrypted ciphertext cannot be cast to string")
	}
	return encryptedKey, nil
}

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

View on GitHub (pinned to 13442bb981)

Solutions

  1. Verify the transit mount path and key name in the SOPS Vault config point to a real transit engine (e.g. /transit/encrypt/<key>).
  2. Test the same encrypt call with `vault write transit/encrypt/<key> plaintext=$(base64 <<< test)` and inspect that the response JSON has data.ciphertext.
  3. Check Vault ACL policies (vault policy read) to ensure the token has update capability on the transit encrypt path and responses are not filtered.
  4. Confirm the Vault server/CLI versions are compatible with the transit API response format used by the api.Secret client.

Example fix

// before: encrypting against a KV mount returns data without ciphertext
keyservice: hcvault://vault.example.com:8200/secret/data/mykey
// after: point at the transit engine and key
keyservice: hcvault://vault.example.com:8200/transit/keys/mykey
Defensive patterns

Strategy: validation

Validate before calling

// Call the Vault transit encrypt endpoint directly and assert the field exists
resp, err := client.Logical().Write("transit/encrypt/"+keyName, map[string]interface{}{"plaintext": b64})
if err != nil { log.Fatal(err) }
if resp == nil || resp.Data == nil {
    log.Fatal("transit response has no data")
}
if _, ok := resp.Data["ciphertext"]; !ok {
    log.Fatal("transit mount returned no ciphertext field; check mount path is a transit engine")
}

Type guard

func hasCiphertext(secret *api.Secret) bool {
    if secret == nil || secret.Data == nil { return false }
    v, ok := secret.Data["ciphertext"]
    if !ok { return false }
    _, isStr := v.(string)
    return isStr
}

Prevention

When it happens

Trigger: Calling EncryptContext (or TestMasterKey_Decrypt) when the Vault transit endpoint returns 200 but the response Data lacks "ciphertext" — e.g. writing to a wrong endpoint path, hitting a non-transit mount, or a Vault version/policy that strips the field.

Common situations: Misconfigured VAULT_TRANSIT_MOUNT or key name pointing at a non-transit secret engine; Vault ACL policies that filter response fields; custom Vault proxy rewriting responses; switching from Vault KV to transit without updating the mount path.

Related errors


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