JuliusBrussee/caveman · error
kms: create request
Error message
kms: create %s request: %w
What it means
call builds the Key Manager endpoint URL and creates the request with http.NewRequestWithContext; if request construction fails, the error is wrapped as "kms: create <operation> request: %w". NewRequestWithContext only fails on an unparseable URL or an invalid method, so this points to a malformed apiBaseURL in the client configuration.
Solutions
- Inspect and fix the apiBaseURL used at construction — it must be an absolute, valid URL like https://api.scaleway.com.
- Trim/expand the env var or config value feeding apiBaseURL (no newlines, no unexpanded templates).
- Validate the URL with url.Parse before constructing the client and fail fast on bad config.
- Unwrap the error chain for the exact url.Parse failure message.
Example fix
// before
client := &kms.Client{apiBaseURL: os.Getenv("SCW_API"), ...} // SCW_API=""
_, err := client.Encrypt(ctx, pt) // kms: create encrypt request: ...
// after
base := strings.TrimSpace(os.Getenv("SCW_API"))
if u, err := url.Parse(base); err != nil || u.Scheme == "" || u.Host == "" {
return fmt.Errorf("invalid SCW_API base URL %q", base)
}
client := &kms.Client{apiBaseURL: base, ...} Defensive patterns
Strategy: validation
Validate before calling
base := strings.TrimSpace(cfg.APIBaseURL)
if u, err := url.Parse(base); err != nil || u.Scheme == "" || u.Host == "" {
return fmt.Errorf("apiBaseURL %q is not an absolute URL", base)
} Type guard
func validBaseURL(s string) bool {
u, err := url.Parse(s)
return err == nil && (u.Scheme == "http" || u.Scheme == "https") && u.Host != ""
} Try / catch
_, err := client.Encrypt(ctx, plaintext)
if err != nil {
if strings.Contains(err.Error(), "create encrypt request") {
return fmt.Errorf("malformed KMS endpoint config: %w", err)
}
return err
} Prevention
- Validate apiBaseURL with url.Parse at client construction.
- Trim env/config values feeding the base URL (strip whitespace/newlines).
- Fail startup fast if the templated URL still contains placeholders.
- Only use https:// absolute URLs for the Key Manager endpoint.
When it happens
Trigger: Calling Encrypt or Decrypt when c.apiBaseURL is empty, contains spaces/control characters, or is otherwise unparseable, making the concatenation an invalid URL; the operation path component itself is escaped, so the base URL is the usual culprit.
Common situations: Configuring apiBaseURL from an env var with a trailing newline or whitespace; missing scheme (e.g. "scaleway.api" instead of "https://..."); config templating leaving a placeholder like "{{API_URL}}" unexpanded.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- compat request URL is missing
- compat upstream base_url
- kms: invalid API base URL
- kms: read response
- kms: request failed
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/53e7ec0bebbacc2f.
Report an issue: GitHub.
Appendix: source
Thrown at shared/platform/kms/kms.go:355
if !regionPattern.MatchString(region) {
return errors.New("kms: invalid Scaleway region")
}
if !keyIDPattern.MatchString(keyID) {
return errors.New("kms: invalid Scaleway key ID")
}
return nil
}
func (c *Client) call(ctx context.Context, region, keyID, operation string, input, output any) error {
body, err := json.Marshal(input)
if err != nil {
return fmt.Errorf("kms: encode %s request: %w", operation, err)
}
endpoint := c.apiBaseURL + "/key-manager/v1alpha1/regions/" + url.PathEscape(region) +
"/keys/" + url.PathEscape(keyID) + "/" + operation
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(body))
if err != nil {
return fmt.Errorf("kms: create %s request: %w", operation, err)
}
req.Header.Set("content-type", "application/json")
req.Header.Set("accept", "application/json")
req.Header.Set("x-auth-token", c.token)
resp, err := c.httpClient.Do(req)
if err != nil {
return fmt.Errorf("kms: %s request failed: %w", operation, err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
_, _ = io.Copy(io.Discard, io.LimitReader(resp.Body, 32<<10))
return fmt.Errorf("kms: %s returned HTTP %d", operation, resp.StatusCode)
}
data, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes+1))
if err != nil {
return fmt.Errorf("kms: read %s response: %w", operation, err)
}
if len(data) > maxResponseBytes {View on GitHub (pinned to 3ee70a1026)