getsops/sops · error

failed to construct Azure Key Vault client to decrypt data:

Error message

failed to construct Azure Key Vault client to decrypt data: %w

What it means

Raised in DecryptContext when azkeys.NewClient fails to construct the Azure Key Vault client, typically because the VaultURL is not a valid URL. Client construction happens after credential acquisition and base64 decoding, so this points at the vault URL configuration rather than auth.

Source

Thrown at azkv/keysource.go:278

// DecryptContext decrypts the EncryptedKey field with Azure Key Vault and returns
// the result.
func (key *MasterKey) DecryptContext(ctx context.Context) ([]byte, error) {
	token, err := key.getTokenCredential()
	if err != nil {
		log.WithFields(logrus.Fields{"key": key.Name, "version": key.Version}).Info("Decryption failed")
		return nil, fmt.Errorf("failed to get Azure token credential to decrypt: %w", err)
	}

	rawEncryptedKey, err := base64.RawURLEncoding.DecodeString(key.EncryptedKey)
	if err != nil {
		log.WithFields(logrus.Fields{"key": key.Name, "version": key.Version}).Info("Decryption failed")
		return nil, fmt.Errorf("failed to base64 decode Azure Key Vault encrypted key: %w", err)
	}

	c, err := azkeys.NewClient(key.VaultURL, token, key.clientOptions)
	if err != nil {
		log.WithFields(logrus.Fields{"key": key.Name, "version": key.Version}).Info("Decryption failed")
		return nil, fmt.Errorf("failed to construct Azure Key Vault client to decrypt data: %w", err)
	}

	resp, err := c.Decrypt(ctx, key.Name, key.Version, azkeys.KeyOperationParameters{
		Algorithm: to.Ptr(azkeys.EncryptionAlgorithmRSAOAEP256),
		Value:     rawEncryptedKey,
	}, nil)
	if err != nil {
		log.WithFields(logrus.Fields{"key": key.Name, "version": key.Version}).Info("Decryption failed")
		return nil, fmt.Errorf("failed to decrypt sops data key with Azure Key Vault key '%s': %w", key.ToString(), err)
	}
	log.WithFields(logrus.Fields{"key": key.Name, "version": key.Version}).Info("Decryption succeeded")
	return resp.KeyOperationResult.Result, nil
}

// NeedsRotation returns whether the data key needs to be rotated or not.
func (key *MasterKey) NeedsRotation() bool {
	return time.Since(key.CreationDate) > (azkvTTL)
}

View on GitHub (pinned to 13442bb981)

Solutions

  1. Set the full vault URL in .sops.yaml, e.g. vault_url: 'https://<vault-name>.vault.azure.net/'
  2. Check that the config key field is not empty or mis-indented so it parses into MasterKey.VaultURL
  3. Fix templating/env substitution that left the vault URL blank

Example fix

// before
azure_kv:
  - vault_url: myvault
// after
azure_kv:
  - vault_url: 'https://myvault.vault.azure.net/'
    key: mykey
Defensive patterns

Strategy: validation

Validate before calling

u, err := url.Parse(key.VaultURL)
valid := err == nil && u.Scheme == "https" && strings.HasSuffix(u.Hostname(), ".vault.azure.net")
if !valid { return fmt.Errorf("invalid vault_url %q; use https://<name>.vault.azure.net/", key.VaultURL) }

Try / catch

if err := masterKey.Decrypt(); err != nil {
	if strings.Contains(err.Error(), "failed to construct Azure Key Vault client") {
		return fmt.Errorf("check vault_url in .sops.yaml: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: Calling Decrypt on an Azure KV MasterKey whose VaultURL field is empty, malformed (missing scheme, stray characters), or otherwise rejected by azkeys.NewClient.

Common situations: Missing vault_url in the .sops.yaml Azure KV key entry, hand-written vault URL like 'myvault' instead of 'https://myvault.vault.azure.net/', templated config where the vault URL variable resolved empty.

Related errors


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