JuliusBrussee/caveman · error

kms: unsupported provider %q

Error message

kms: unsupported provider %q

What it means

kms.New only accepts the Scaleway provider: it lowercases and trims cfg.Provider, then requires it to equal ProviderScaleway ("scaleway"). Any other value — including empty — is rejected before region/key validation. The client is deliberately single-provider so envelope metadata can never redirect decryption to another KMS host.

Source

Thrown at shared/platform/kms/kms.go:78

	token         string
	apiBaseURL    string
	httpClient    *http.Client
	decryptKeyIDs map[string]struct{}
}

// Envelope is safe to persist. Ciphertext is opaque provider output.
type Envelope struct {
	Provider   string `json:"provider"`
	Region     string `json:"region"`
	KeyID      string `json:"key_id"`
	Ciphertext string `json:"ciphertext"`
}

// New validates configuration and returns immutable client.
func New(cfg Config) (*Client, error) {
	provider := strings.ToLower(strings.TrimSpace(cfg.Provider))
	if provider != ProviderScaleway {
		return nil, fmt.Errorf("kms: unsupported provider %q", provider)
	}
	region, keyID := strings.TrimSpace(cfg.Region), strings.TrimSpace(cfg.KeyID)
	if err := validateLocation(region, keyID); err != nil {
		return nil, err
	}
	decryptKeyIDs := map[string]struct{}{keyID: {}}
	for _, allowedKeyID := range cfg.AllowedDecryptKeyIDs {
		allowedKeyID = strings.TrimSpace(allowedKeyID)
		if err := validateLocation(region, allowedKeyID); err != nil {
			return nil, err
		}
		decryptKeyIDs[allowedKeyID] = struct{}{}
	}
	token := strings.TrimSpace(cfg.AuthToken)
	if len(token) < 20 {
		return nil, errors.New("kms: auth token is required")
	}
	baseURL := strings.TrimRight(strings.TrimSpace(cfg.APIBaseURL), "/")

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Set cfg.Provider to exactly "scaleway" (case/whitespace tolerated)
  2. If the provider env var is empty in production, verify the variable name and that the secret/config map actually injects it
  3. If you need another KMS, this package does not support it — wrap at a higher layer or extend ProviderScaleway handling consciously
  4. Add config validation at startup so this fails before first Encrypt/Decrypt call

Example fix

// before
cfg := kms.Config{Provider: "scw", Region: "fr-par", KeyID: keyID}
client, err := kms.New(cfg)

// after
cfg := kms.Config{Provider: kms.ProviderScaleway, Region: "fr-par", KeyID: keyID}
client, err := kms.New(cfg)
Defensive patterns

Strategy: validation

Validate before calling

func validKMSConfig(cfg kms.Config) bool {
	return strings.ToLower(strings.TrimSpace(cfg.Provider)) == kms.ProviderScaleway
}

Type guard

func isSupportedProvider(p string) bool {
	return strings.ToLower(strings.TrimSpace(p)) == "scaleway"
}

Try / catch

if _, err := kms.New(cfg); err != nil { if strings.Contains(err.Error(), "unsupported provider") { return fmt.Errorf("KMS provider must be %q, got %q", kms.ProviderScaleway, cfg.Provider) } }

Prevention

When it happens

Trigger: Constructing Config{Provider: "aws"} or "gcp" or ""; passing "Scaleway " works (trimmed/lowercased) but "scw" or "scaleway-kms" fails; config loaded from env where the provider variable was never set.

Common situations: Template config copied from a multi-provider example; env var naming mismatch (KMS_PROVIDER unset in the deployment); migration from another KMS leaving the old provider string in config; YAML indentation putting provider under the wrong block so it stays empty.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/eb36db1d98b2948f. Report an issue: GitHub.