JuliusBrussee/caveman · error

kms: %s request failed: %w

Error message

kms: %s request failed: %w

What it means

Inside the low-level call helper: the POST to {apiBaseURL}/key-manager/v1alpha1/regions/{region}/keys/{keyID}/{operation} failed at the transport level (httpClient.Do returned an error). The %w wraps Go net/http errors — DNS failure, connection refused, TLS handshake problems, timeouts, or context cancellation. This is distinct from 1198, which fires when the request completes but the status is not 200.

Source

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

}

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 {
		return fmt.Errorf("kms: %s response exceeds limit", operation)
	}
	if err := json.Unmarshal(data, output); err != nil {
		return fmt.Errorf("kms: decode %s response: %w", operation, err)
	}
	return nil
}

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Read the wrapped net/http error — 'connection refused'/'no such host' point at network, 'context deadline exceeded' at your timeout
  2. Verify egress to the key-manager API endpoint is allowed (firewall, NetworkPolicy, proxy)
  3. If behind a TLS-inspecting proxy, add its CA to the client's trust store or configure the http.Client accordingly
  4. Increase or remove an overly tight context deadline on the KMS call path

Example fix

// before
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
deferred := cancel
_ = client.Encrypt(ctx, plaintext)

// after
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_ = client.Encrypt(ctx, plaintext)
Defensive patterns

Strategy: retry

Validate before calling

func canReachKMS(ctx context.Context, endpoint string) bool {
	req, _ := http.NewRequestWithContext(ctx, http.MethodHead, endpoint, nil)
	resp, err := http.DefaultClient.Do(req)
	if err != nil { return false }
	resp.Body.Close()
	return true
}

Try / catch

var pt []byte
err := backoffRetry(func() error {
	var e error
	pt, e = client.Encrypt(ctx, plaintext)
	return e
}) // retry only on transport errors (kms: ... request failed)

Prevention

When it happens

Trigger: DNS for the Scaleway API not resolvable from the container; connection refused by an egress firewall; TLS certificate validation failing behind a MITM proxy; context cancelled/deadline exceeded because the caller's ctx timed out; proxy misconfiguration in HTTP_PROXY env vars.

Common situations: Kubernetes cluster without egress rules allowing the KMS endpoint; corporate MITM proxy whose CA is not in the trust store; short client-side context deadlines; local dev on VPN with split DNS not resolving internal/API names.

Related errors


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