JuliusBrussee/caveman · error

kms: decrypt response plaintext is not valid base64

Error message

kms: decrypt response plaintext is not valid base64

What it means

Decrypt validates the KMS response and base64-decodes response.Plaintext. When the server (or a tampering intermediary) returns a Plaintext string that is not valid standard base64, decoding fails and this error replaces the raw decode error to keep the failure non-leaky.

Solutions

  1. Verify the response comes from the real Scaleway endpoint (no proxies re-encoding the body)
  2. If using a mock, ensure it encodes plaintext with base64.StdEncoding
  3. Log the raw response (in a safe environment) to confirm the malformed field; report unexpected server payloads to Scaleway if genuine

Example fix

// mock fix
// before
resp.Plaintext = string(plaintext)
// after
resp.Plaintext = base64.StdEncoding.EncodeToString(plaintext)
Defensive patterns

Strategy: try-catch

Validate before calling

// validate response before use (if you control the mock/producer)
if _, err := base64.StdEncoding.DecodeString(resp.Plaintext); err != nil {
	return fmt.Errorf("producer emitted invalid base64: %w", err)
}

Type guard

func isValidStdBase64(s string) bool {
	_, err := base64.StdEncoding.DecodeString(s)
	return err == nil
}

Try / catch

plaintext, err := client.Decrypt(ctx, envelope)
if err != nil {
	if err.Error() == "kms: decrypt response plaintext is not valid base64" {
		// inspect/refresh envelope, alert on possible tampering
	}
	return fmt.Errorf("decrypt: %w", err)
}

Prevention

When it happens

Trigger: KMS endpoint or an interceptor returning malformed/corrupted plaintext field, envelope KeyID matching but plaintext field mangled (e.g. URL-safe base64 or raw bytes instead of StdEncoding), proxy rewriting the response.

Common situations: Custom fake/mock KMS servers in tests emitting wrong base64 variant, middleware that re-encodes responses, or hitting the wrong API version/endpoint returning an unexpected payload shape.

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/4a626a6cceea17b2. Report an issue: GitHub.

Appendix: source

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

	}
	if strings.TrimSpace(envelope.Ciphertext) == "" {
		return nil, errors.New("kms: envelope ciphertext is empty")
	}
	var response struct {
		KeyID     string `json:"key_id"`
		Plaintext string `json:"plaintext"`
	}
	if err := c.call(ctx, envelope.Region, envelope.KeyID, "decrypt", map[string]string{
		"ciphertext": envelope.Ciphertext,
	}, &response); err != nil {
		return nil, err
	}
	if response.KeyID != envelope.KeyID || response.Plaintext == "" {
		return nil, errors.New("kms: invalid decrypt response")
	}
	plaintext, err := base64.StdEncoding.DecodeString(response.Plaintext)
	if err != nil {
		return nil, errors.New("kms: decrypt response plaintext is not valid base64")
	}
	if len(plaintext) == 0 || len(plaintext) > maxPlaintextBytes {
		return nil, errors.New("kms: decrypt response plaintext exceeds size limit")
	}
	return plaintext, nil
}

// ValidateProduction verifies real KMS configuration without network request.
func ValidateProduction() error {
	if !runtimeenv.IsProduction() {
		return nil
	}
	_, err := FromEnvironment()
	return err
}

// ValidatePayloadProduction verifies the dedicated artifact-payload KEK is
// configured. This is separate from ValidateProduction because control-plane

View on GitHub (pinned to 3ee70a1026)