{"record":{"id":"5f758d304a9404af","repo":"JuliusBrussee/caveman","slug":"secretbox-kms-decrypt-w","errorCode":null,"errorMessage":"secretbox: KMS decrypt: %w","messagePattern":"secretbox: KMS decrypt: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"shared/platform/secretbox/secretbox.go","lineNumber":110,"sourceCode":"\t\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\t\tdefer cancel()\n\t\twrapped, err := kms.EncryptPayload(ctx, plaintext)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"secretbox: payload KMS encrypt: %w\", err)\n\t\t}\n\t\treturn wrapped, nil\n\t}\n\treturn Encrypt(plaintext)\n}\n\n// Decrypt reverses Encrypt: it expects nonce(12) || ciphertext+tag.\nfunc Decrypt(envelope []byte) ([]byte, error) {\n\tif kms.IsEnvelope(envelope) {\n\t\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\t\tdefer cancel()\n\t\tplaintext, err := kms.Decrypt(ctx, envelope)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"secretbox: KMS decrypt: %w\", err)\n\t\t}\n\t\treturn plaintext, nil\n\t}\n\tif runtimeenv.IsProduction() &&\n\t\t!strings.EqualFold(strings.TrimSpace(os.Getenv(\"CAVE_KMS_ALLOW_LEGACY_LOCAL_DECRYPT\")), \"true\") {\n\t\treturn nil, fmt.Errorf(\"secretbox: production refuses legacy local ciphertext\")\n\t}\n\tkeyBytes, err := loadKey()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tblock, err := aes.NewCipher(keyBytes)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"aes cipher: %w\", err)\n\t}\n\tgcm, err := cipher.NewGCM(block)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"aes-gcm: %w\", err)","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/secretbox/secretbox.go#L92-L128","documentation":"secretbox.Decrypt first checks kms.IsEnvelope(envelope); if the ciphertext is a KMS envelope, it calls kms.Decrypt with a 10s timeout and wraps any failure as 'secretbox: KMS decrypt'. The underlying error (bad credentials, envelope from a different key/region, tampering, or timeout) is preserved via %w. Decryption fails closed — there is no local fallback for KMS envelopes.","triggerScenarios":"Calling Decrypt (directly or via DecryptPayloadKey / ResolveEnvironmentSecret) with a KMS-format envelope when kms.Decrypt errors: credentials lacking decrypt permission on the key, envelope produced in a different KMS key/region than the one configured, KMS outage, or >10s latency.","commonSituations":"Data encrypted in one environment/region decrypted in another with a different key configured; IAM policy revoked the runtime identity's kms-decrypt right; Scaleway credentials rotated without redeploy; cross-account key usage not granted.","solutions":["Unwrap the error to see the KMS cause (permission denied, not found, timeout, etc.).","Confirm CAVE_KMS_PROVIDER credentials and key configuration match the key that produced the envelope (same key ID/region).","Check IAM/KMS ACLs: the runtime identity must have decrypt rights on the key.","If envelopes were created under an old key, decrypt with the old key configured or re-encrypt data under the new key.","Retry after transient KMS outages; check endpoint reachability for network causes."],"exampleFix":"// before: envelope from staging key decrypted in prod -> secretbox: KMS decrypt: not found\n CAVE_KMS_KEY_ID=prod-key\n\n// after: point decryptor at the key that wrapped the payload, or migrate data\n CAVE_KMS_KEY_ID=staging-key  # or re-encrypt staging data under prod-key","handlingStrategy":"try-catch","validationCode":"if kms.IsEnvelope(envelope) {\n    if os.Getenv(\"CAVE_KMS_PROVIDER\") == \"\" {\n        return errors.New(\"KMS envelope encountered but no CAVE_KMS_PROVIDER configured\")\n    }\n}","typeGuard":null,"tryCatchPattern":"pt, err := secretbox.Decrypt(envelope)\nif err != nil {\n    if strings.Contains(err.Error(), \"KMS decrypt\") {\n        switch {\n        case errors.Is(err, context.DeadlineExceeded):\n            return retry(envelope) // transient latency\n        default:\n            // likely key/permission mismatch: do not retry blindly\n            return fmt.Errorf(\"cannot decrypt envelope: %w\", err)\n        }\n    }\n    return err\n}","preventionTips":["Track which KMS key encrypted each record (key ID in metadata) and configure the matching key before decrypting.","Grant the runtime identity decrypt permission on every key that produced live envelopes.","Rotate credentials with coordinated redeployment, not in-place secret edits.","Run a decrypt self-test on a known envelope in startup health checks."],"tags":["kms","decryption","key-management","permissions","go"],"backgroundTag":"api-error-response","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}