{"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":"error","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/27d5a3981a347890211bb1bf2439e5c821a63bc9/shared/platform/secretbox/secretbox.go#L92-L128","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\npt, err := secretbox.Decrypt(env)\nif err != nil {\n    log.Fatalf(\"decrypt failed: %v\", err) // opaque\n}\n\n// after\npt, err := secretbox.Decrypt(env)\nif err != nil {\n    var kerr *kms.Error // or inspect errors.Unwrap chain\n    if errors.As(err, &kerr) {\n        log.Printf(\"kms code=%s key=%s\", kerr.Code, kerr.KeyID)\n    }\n    if strings.Contains(err.Error(), \"context deadline exceeded\") {\n        // retry once after verifying egress to the KMS endpoint\n    }\n    return err\n}","handlingStrategy":"try-catch","validationCode":"if kms.IsEnvelope(data) {\n    // will hit the KMS path; ensure KMS client is configured and reachable first\n    if err := kms.HealthCheck(ctx); err != nil {\n        return fmt.Errorf(\"kms not ready: %w\", err)\n    }\n}","typeGuard":"func isKmsDecryptErr(err error) bool {\n    return err != nil && strings.HasPrefix(err.Error(), \"secretbox: KMS decrypt:\")\n}","tryCatchPattern":"pt, err := secretbox.Decrypt(env)\nif err != nil {\n    if isKmsDecryptErr(err) {\n        if errors.Is(err, context.DeadlineExceeded) {\n            // retry once: KMS timeout is often transient\n            pt, err = secretbox.Decrypt(env)\n        }\n    }\n    if err != nil {\n        return fmt.Errorf(\"decrypt envelope: %w\", err)\n    }\n}","preventionTips":["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."],"tags":["crypto","kms","secretbox","go"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}