JuliusBrussee/caveman · error
secretbox: KMS decrypt: %w
Error message
secretbox: KMS decrypt: %w
What it means
Thrown by secretbox.Decrypt when the input is a KMS envelope (kms.IsEnvelope matched) but the KMS Decrypt call fails. The KMS operation runs with a hard 10-second context timeout, so both real KMS failures (bad key id, denied permissions, corrupted envelope) and timeouts surface here wrapped with the 'secretbox: KMS decrypt:' prefix. The underlying %w error carries the KMS-specific detail.
Source
Thrown at shared/platform/secretbox/secretbox.go:110
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
wrapped, err := kms.EncryptPayload(ctx, plaintext)
if err != nil {
return nil, fmt.Errorf("secretbox: payload KMS encrypt: %w", err)
}
return wrapped, nil
}
return Encrypt(plaintext)
}
// Decrypt reverses Encrypt: it expects nonce(12) || ciphertext+tag.
func Decrypt(envelope []byte) ([]byte, error) {
if kms.IsEnvelope(envelope) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
plaintext, err := kms.Decrypt(ctx, envelope)
if err != nil {
return nil, fmt.Errorf("secretbox: KMS decrypt: %w", err)
}
return plaintext, nil
}
if runtimeenv.IsProduction() &&
!strings.EqualFold(strings.TrimSpace(os.Getenv("CAVE_KMS_ALLOW_LEGACY_LOCAL_DECRYPT")), "true") {
return nil, fmt.Errorf("secretbox: production refuses legacy local ciphertext")
}
keyBytes, err := loadKey()
if err != nil {
return nil, err
}
block, err := aes.NewCipher(keyBytes)
if err != nil {
return nil, fmt.Errorf("aes cipher: %w", err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("aes-gcm: %w", err)View on GitHub (pinned to 27d5a3981a)
Solutions
- Inspect the wrapped error chain (errors.Unwrap) to see the true KMS cause: NotFound/Disabled key, AccessDenied, or context deadline exceeded.
- If the error is a timeout, verify network egress and KMS endpoint reachability from the service, then retry the decrypt.
- If AccessDenied/NotFound, fix the key policy or re-enable the key referenced by the envelope, or re-encrypt the data under an accessible key.
- If the envelope bytes are corrupt (invalid base64/structure), restore the correct ciphertext from its source of truth rather than patching bytes.
Example fix
// before
pt, err := secretbox.Decrypt(env)
if err != nil {
log.Fatalf("decrypt failed: %v", err) // opaque
}
// after
pt, err := secretbox.Decrypt(env)
if err != nil {
var kerr *kms.Error // or inspect errors.Unwrap chain
if errors.As(err, &kerr) {
log.Printf("kms code=%s key=%s", kerr.Code, kerr.KeyID)
}
if strings.Contains(err.Error(), "context deadline exceeded") {
// retry once after verifying egress to the KMS endpoint
}
return err
} Defensive patterns
Strategy: try-catch
Validate before calling
if kms.IsEnvelope(data) {
// will hit the KMS path; ensure KMS client is configured and reachable first
if err := kms.HealthCheck(ctx); err != nil {
return fmt.Errorf("kms not ready: %w", err)
}
} Type guard
func isKmsDecryptErr(err error) bool {
return err != nil && strings.HasPrefix(err.Error(), "secretbox: KMS decrypt:")
} Try / catch
pt, err := secretbox.Decrypt(env)
if err != nil {
if isKmsDecryptErr(err) {
if errors.Is(err, context.DeadlineExceeded) {
// retry once: KMS timeout is often transient
pt, err = secretbox.Decrypt(env)
}
}
if err != nil {
return fmt.Errorf("decrypt envelope: %w", err)
}
} Prevention
- Verify KMS key access (IAM/policy) in deployment smoke tests before serving traffic.
- Monitor decrypt error rates; spikes after key rotation indicate disabled old keys still referenced by stored envelopes.
- Keep envelopes small and intact — never truncate or re-encode stored ciphertext blobs.
When it happens
Trigger: Calling secretbox.Decrypt on data previously encrypted via secretbox.Encrypt when a KMS envelope was produced, while KMS credentials/key access are missing, the key was rotated/disabled, the envelope bytes were truncated or tampered with, or the KMS API did not answer within 10 seconds.
Common situations: Deploying to an environment where the KMS IAM role/service account differs from the one that encrypted the data; key rotation disabled the old key; a database restore moved ciphertext across KMS regions; network egress to the KMS endpoint blocked so the 10s timeout fires.
Related errors
- secretbox: production refuses legacy local ciphertext
- production KMS configuration: %w
- kms: unsupported provider %q
- kms: plaintext exceeds %d bytes
- kms: decode envelope: %w
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/5f758d304a9404af.
Report an issue: GitHub.