{"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":"error","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/27d5a3981a347890211bb1bf2439e5c821a63bc9/shared/platform/secretbox/secretbox.go#L41-L77","documentation":"secretbox.Encrypt delegated to the KMS (useKMS() true, i.e. CAVE_KMS_PROVIDER configured) and kms.Encrypt returned an error within its 10-second context deadline; the error is wrapped with the 'secretbox: KMS encrypt' prefix so the KMS root cause survives for errors.Is/As. Failures originate in the KMS client: auth, network, throttling, or the deadline itself.","triggerScenarios":"Encrypting a secret while the Scaleway KMS endpoint is unreachable (DNS, firewall, outage), the API credentials are wrong/expired, the key reference points to a deleted/disabled KMS key, or the call exceeded the hard-coded 10s timeout.","commonSituations":"Production deploy with missing/rotated SCW credentials; VPC/firewall blocking the KMS endpoint; the KMS key was deleted after a cleanup but the config still references it; latency spikes making 10s too tight.","solutions":["Check the wrapped error's cause first — status code 401/403 means credentials, 404 means wrong key ID, timeouts mean network/latency.","Verify the Scaleway credentials and key ID configured for the KMS package, and that the endpoint is reachable from the deploy network.","Retry transient failures (throttle/network) with backoff at the caller; if 10s is consistently exceeded, address latency rather than removing the deadline."],"exampleFix":"// before\nct, err := secretbox.Encrypt(secret)\nif err != nil { return err } // surfaces as \"secretbox: KMS encrypt: ...\"\n\n// after\nvar ct []byte\nerr := retry(3, time.Second, func() error {\n    var e error\n    ct, e = secretbox.Encrypt(secret)\n    return e\n})\nif err != nil { return fmt.Errorf(\"seal secret: %w\", err) }","handlingStrategy":"retry","validationCode":"// Preflight the KMS path at startup so config errors surface before traffic:\nfunc probeKMS(ctx context.Context) error {\n    _, err := kms.Encrypt(ctx, []byte(\"healthcheck\"))\n    return err // 401/403/404 => config; timeout => network\n}","typeGuard":null,"tryCatchPattern":"var out []byte\nerr := retry(3, 500*time.Millisecond, func() error {\n    var e error\n    out, e = secretbox.Encrypt(pt)\n    return e\n})\nif err != nil {\n    return fmt.Errorf(\"secretbox encrypt: %w\", err) // inspect cause; only retry transient (network/429)\n}","preventionTips":["Probe KMS reachability and credentials at startup with a cheap encrypt call.","Alert on wrapped 4xx causes — they are configuration drift, not transient failures.","Keep the 10s deadline; if it trips regularly, fix latency instead of removing the bound."],"tags":["secrets","kms","network","crypto"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}