JuliusBrussee/caveman · error

kms: probe plaintext mismatch

Error message

kms: probe plaintext mismatch

What it means

Probe performs an encrypt-then-decrypt round trip against the configured key; if the bytes returned by Decrypt do not equal the original plaintext it reports a mismatch. This indicates the KMS path is not behaving transparently and should never happen with a healthy Scaleway endpoint.

Solutions

  1. Point the client at the genuine Scaleway KMS endpoint and a valid key
  2. Fix the mock/test double so Decrypt returns exactly the bytes passed to Encrypt
  3. Inspect any proxy/TLS-terminating middleware that could corrupt the payload

Example fix

// mock fix
// before
return ciphertext[:len(ciphertext)-1], nil // truncated
// after
return plaintext, nil // store and return original plaintext
Defensive patterns

Strategy: try-catch

Try / catch

if err := client.Probe(ctx); err != nil {
	if strings.Contains(err.Error(), "probe plaintext mismatch") {
		log.Error("KMS round trip corrupted — check endpoint/mocks/proxies")
	}
	return err
}

Prevention

When it happens

Trigger: Running Probe against a mocked or faulty KMS endpoint whose decrypt output differs from encrypt input, corrupted responses, or an interceptor/test double that mangles payloads.

Common situations: Testing with a hand-rolled fake KMS, network middleboxes altering bodies, or a misconfigured base URL pointing at an incompatible KMS-compatible API.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/2cd3532147d03fdc. Report an issue: GitHub.

Appendix: source

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

	return client.Probe(ctx)
}

// Probe verifies live key access without persisting tenant data.
func (c *Client) Probe(ctx context.Context) error {
	plaintext := make([]byte, 32)
	if _, err := rand.Read(plaintext); err != nil {
		return fmt.Errorf("kms: generate probe: %w", err)
	}
	envelope, err := c.Encrypt(ctx, plaintext)
	if err != nil {
		return fmt.Errorf("kms: probe encrypt: %w", err)
	}
	decrypted, err := c.Decrypt(ctx, envelope)
	if err != nil {
		return fmt.Errorf("kms: probe decrypt: %w", err)
	}
	if !bytes.Equal(decrypted, plaintext) {
		return errors.New("kms: probe plaintext mismatch")
	}
	return nil
}

func validateLocation(region, keyID string) error {
	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)

View on GitHub (pinned to 3ee70a1026)