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
- Set cfg.Provider to exactly "scaleway" (case/whitespace tolerated)
- If the provider env var is empty in production, verify the variable name and that the secret/config map actually injects it
- If you need another KMS, this package does not support it — wrap at a higher layer or extend ProviderScaleway handling consciously
- 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
- Default Provider to kms.ProviderScaleway in config loading when unset
- Fail fast at startup with a config validation pass
- Keep provider in a single config location, not duplicated per environment
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
- production KMS configuration: %w
- invalid listen address %q: %w
- invalid listen port %q
- %s: load system certificate pool: %w
- %s (%s): certificate %d is unparseable, so the bundle is inc
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/eb36db1d98b2948f.
Report an issue: GitHub.