{"record":{"id":"5a86c7edc0f22ba4","repo":"JuliusBrussee/caveman","slug":"secretbox-kms-encrypt-w","errorCode":null,"errorMessage":"secretbox: KMS encrypt: %w","messagePattern":"secretbox: KMS encrypt: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"shared/platform/secretbox/secretbox.go","lineNumber":59,"sourceCode":"\tkeyBytes, err := base64.StdEncoding.DecodeString(keyB64)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s is not valid base64: %w\", envKey, err)\n\t}\n\tif len(keyBytes) != 32 {\n\t\treturn nil, fmt.Errorf(\"%s must decode to exactly 32 bytes, got %d\", envKey, len(keyBytes))\n\t}\n\treturn keyBytes, nil\n}\n\n// Encrypt seals plaintext with AES-256-GCM and a fresh random nonce, returning\n// nonce(12) || ciphertext+tag as raw bytes.\nfunc Encrypt(plaintext []byte) ([]byte, error) {\n\tif useKMS() {\n\t\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\t\tdefer cancel()\n\t\twrapped, err := kms.Encrypt(ctx, plaintext)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"secretbox: KMS encrypt: %w\", err)\n\t\t}\n\t\treturn wrapped, nil\n\t}\n\tif runtimeenv.IsProduction() {\n\t\treturn nil, fmt.Errorf(\"secretbox: production requires CAVE_KMS_PROVIDER=scaleway\")\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)\n\t}","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/secretbox/secretbox.go#L41-L77","documentation":"secretbox.Encrypt detected that KMS-based encryption is enabled (CAVE_KMS_PROVIDER is set) and delegated the encryption to the configured KMS provider, but the kms.Encrypt call failed. The underlying KMS error (network failure, bad credentials, misconfigured key) is wrapped with %w so the cause is preserved. This error means the envelope could not be produced at all.","triggerScenarios":"Calling secretbox.Encrypt (directly or via EncryptPayloadKey falling through) while useKMS() is true and kms.Encrypt returns a non-nil error, e.g. expired Scaleway credentials, unreachable KMS endpoint, or an invalid CAVE_KMS_* key configuration. The call has a hard 10s context timeout, so KMS latency over 10 seconds also produces this.","commonSituations":"Deployed environment has CAVE_KMS_PROVIDER=scaleway but the access/secret key env vars are wrong or rotated; KMS API outage or network egress blocked from the container; KMS request exceeding the 10-second timeout; wrong KMS region/key ID configured.","solutions":["Check the wrapped cause: print err with %v or errors.Unwrap to see the underlying KMS failure (auth vs network vs timeout).","Verify CAVE_KMS_PROVIDER and the provider credential env vars (e.g. Scaleway access key, secret key, region) are present and current.","Test network egress to the KMS endpoint from the host/container; confirm no firewall or VPC rule blocks it.","If credentials were rotated, redeploy with the new values and re-run the failing operation.","For local development where KMS is unnecessary, unset CAVE_KMS_PROVIDER so secretbox falls back to local AES-GCM (not allowed in production)."],"exampleFix":"// before (missing/rotated credentials)\nos.Setenv(\"CAVE_KMS_PROVIDER\", \"scaleway\")\n// encrypted, err := secretbox.Encrypt(plaintext) -> secretbox: KMS encrypt: ...\n\n// after (valid creds exported, or local mode outside production)\nos.Setenv(\"CAVE_KMS_ACCESS_KEY\", scwAccessKey)\nos.Setenv(\"CAVE_KMS_SECRET_KEY\", scwSecretKey)\n// or, in dev only: os.Unsetenv(\"CAVE_KMS_PROVIDER\")","handlingStrategy":"try-catch","validationCode":"if os.Getenv(\"CAVE_KMS_PROVIDER\") != \"\" {\n    for _, v := range []string{\"CAVE_KMS_ACCESS_KEY\", \"CAVE_KMS_SECRET_KEY\"} {\n        if os.Getenv(v) == \"\" {\n            return fmt.Errorf(\"%s must be set when CAVE_KMS_PROVIDER is configured\", v)\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"wrapped, err := secretbox.Encrypt(plaintext)\nif err != nil {\n    var kmsErr *kms.Error\n    if errors.As(err, &kmsErr) {\n        // alert: KMS unavailable; fail closed, do not fall back to local keys\n        return fmt.Errorf(\"encrypt unavailable: %w\", err)\n    }\n    return err\n}","preventionTips":["Run a startup health check that performs a tiny KMS encrypt/decrypt round trip before serving traffic.","Inject KMS credentials via a secret manager with rotation-aware restart, never hardcode them.","Monitor KMS API latency and error rates; alert before the 10s timeout is routinely hit.","Keep dev and prod provider config separate so local runs never touch production KMS."],"tags":["kms","encryption","network","go"],"backgroundTag":"api-request-failed","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"}