getsops/sops · error

failed to decrypt sops data key from Vault transit backend '

Error message

failed to decrypt sops data key from Vault transit backend '%s': %w

What it means

This error wraps any failure from the Vault API when SOPS writes the encrypted data key to the transit decrypt endpoint (`<enginePath>/decrypt/<keyName>`) during MasterKey.DecryptContext. The Vault server rejected or could not perform the decryption (auth, permissions, bad ciphertext, connectivity).

Source

Thrown at hcvault/keysource.go:292

// Consider using DecryptContext instead.
func (key *MasterKey) Decrypt() ([]byte, error) {
	return key.DecryptContext(context.Background())
}

// DecryptContext decrypts the EncryptedKey field with Vault Transit and returns the result.
func (key *MasterKey) DecryptContext(ctx context.Context) ([]byte, error) {
	fullPath := key.decryptPath()

	client, err := vaultClient(key.VaultAddress, key.token, key.httpClient)
	if err != nil {
		log.WithField("Path", fullPath).Info("Decryption failed")
		return nil, err
	}

	secret, err := client.Logical().WriteWithContext(ctx, fullPath, decryptPayload(key.EncryptedKey))
	if err != nil {
		log.WithField("Path", fullPath).Info("Decryption failed")
		return nil, fmt.Errorf("failed to decrypt sops data key from Vault transit backend '%s': %w", fullPath, err)
	}
	dataKey, err := dataKeyFromSecret(secret)
	if err != nil {
		log.WithField("Path", fullPath).Info("Decryption failed")
		return nil, fmt.Errorf("failed to decrypt sops data key from Vault transit backend '%s': %w", fullPath, err)
	}

	log.WithField("Path", fullPath).Info("Decryption successful")
	return dataKey, nil
}

// NeedsRotation returns whether the data key needs to be rotated or not.
func (key *MasterKey) NeedsRotation() bool {
	// TODO: manage rewrapping https://www.vaultproject.io/api/secret/transit/index.html#rewrap-data
	return time.Since(key.CreationDate) > (vaultTTL)
}

// ToString converts the key to a string representation.

View on GitHub (pinned to 13442bb981)

Solutions

  1. Confirm the token can decrypt: `vault token lookup` and policy with `update` on `<enginePath>/decrypt/*`.
  2. Check the .sops file's `enc` value is a valid vault ciphertext (starts with `vault:v1:`) and non-empty.
  3. Ensure KeyName/EnginePath in the URI match the key that originally encrypted the data; if the key was renamed, update the sops config or re-encrypt.
  4. Test manually: `vault write transit/decrypt/<keyName> ciphertext=vault:v1:...`.
  5. Verify server reachability/TLS as with the encrypt error (vault status, curl health).

Example fix

// before: sops file encrypted with key 'sops-old' but URI says 'sops'
hc_vault: https://vault.example.com/v1/transit/keys/sops
// after: URI matching the key that produced the stored ciphertext
hc_vault: https://vault.example.com/v1/transit/keys/sops-old
Defensive patterns

Strategy: try-catch

Validate before calling

// Go: pre-check the stored ciphertext looks like a vault transit blob
enc := key.EncryptedKey
if enc == "" || !strings.HasPrefix(enc, "vault:v") {
	return fmt.Errorf("encrypted key is not a valid vault ciphertext")
}

Try / catch

dataKey, err := key.DecryptContext(ctx)
if err != nil {
	var ve *api.ResponseError
	if errors.As(err, &ve) && ve.StatusCode == 403 {
		return fmt.Errorf("token lacks transit decrypt permission on %s: %w", fullPath, err)
	}
	return fmt.Errorf("vault decrypt failed (check key name matches encrypting key): %w", err)
}

Prevention

When it happens

Trigger: client.Logical().WriteWithContext(ctx, fullPath, decryptPayload(key.EncryptedKey)) returns an error in DecryptContext — 403 permission denied, 404 unknown key, 400 'invalid ciphertext' or empty ciphertext, or transport error.

Common situations: Token lacks `update` on transit/decrypt/*; the encrypted key was produced by a different transit key than the one configured in the sops file (key rotated/renamed); EncryptedKey empty or corrupted in the .sops.yaml; KeyName changed in Vault; network/TLS issues.

Related errors


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