{"record":{"id":"0e79526eac2ef257","repo":"JuliusBrussee/caveman","slug":"secretbox-payload-kms-encrypt-w","errorCode":null,"errorMessage":"secretbox: payload KMS encrypt: %w","messagePattern":"secretbox: payload KMS encrypt: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/secretbox/secretbox.go","lineNumber":96,"sourceCode":"\tnonce := make([]byte, gcm.NonceSize())\n\tif _, err := rand.Read(nonce); err != nil {\n\t\treturn nil, fmt.Errorf(\"nonce entropy: %w\", err)\n\t}\n\t// Seal appends the ciphertext+tag to nonce, so the returned slice is the\n\t// full nonce||ciphertext envelope.\n\treturn gcm.Seal(nonce, nonce, plaintext, nil), nil\n}\n\n// EncryptPayloadKey wraps an artifact data-encryption key. Production uses the\n// dedicated payload KEK; local development retains the same AES-GCM envelope as\n// other local secrets.\nfunc EncryptPayloadKey(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.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() &&","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/shared/platform/secretbox/secretbox.go#L78-L114","documentation":"EncryptPayloadKey — which wraps an artifact data-encryption key (DEK) under the payload KEK — took the KMS branch and kms.EncryptPayload failed within its 10s context. Distinct from the general secret path: it uses the dedicated payload key in the KMS rather than the general-purpose key, and local development falls back to the same AES-GCM envelope (so this error is production/KMS-only). The %w wrap preserves the KMS cause.","triggerScenarios":"Uploading/storing an encrypted artifact in KMS mode: the payload-specific key reference is wrong or disabled, Scaleway credentials lack permission on that key, the KMS API is unreachable from the service, or the wrap call exceeded the 10s deadline.","commonSituations":"The payload KEK was created in one Scaleway project/region and the service points at another; IAM policy grants access to the general key but not the payload key; regional KMS outage; per-artifact upload volume hitting KMS throttles.","solutions":["Verify the payload key ID/region configured for kms.EncryptPayload exists and the credentials can use it (a quick test wrap via the Scaleway CLI or a probe call).","Inspect the wrapped cause for HTTP status: 403 = IAM, 404 = wrong key/region, 429 = throttle, timeout = network/latency.","For latency-sensitive artifact flows, cache per-process what can be cached (or batch wraps) and retry transient causes with backoff at the caller."],"exampleFix":"// before\nwrapped, err := secretbox.EncryptPayloadKey(dek)\nif err != nil { return err } // \"secretbox: payload KMS encrypt: ...\"\n\n// after\nvar wrapped []byte\nif err := retry(3, 250*time.Millisecond, func() error {\n    var e error\n    wrapped, e = secretbox.EncryptPayloadKey(dek)\n    return e\n}); err != nil {\n    return fmt.Errorf(\"wrap artifact DEK: %w\", err)\n}","handlingStrategy":"retry","validationCode":"// Startup probe for the payload KEK specifically:\nfunc probePayloadKEK(ctx context.Context) error {\n    wrapped, err := kms.EncryptPayload(ctx, []byte(\"probe\"))\n    if err != nil { return err }\n    _, err = kms.Decrypt(ctx, wrapped)\n    return err\n}","typeGuard":null,"tryCatchPattern":"var wrapped []byte\nerr := retry(3, 250*time.Millisecond, func() error {\n    var e error\n    wrapped, e = secretbox.EncryptPayloadKey(dek)\n    return e\n})\nif err != nil { return fmt.Errorf(\"wrap DEK: %w\", err) } // retry only network/429 causes","preventionTips":["Verify the payload key's ID, project, and region — it is separate from the general-use key.","Grant IAM permission for both keys; a policy covering only one causes exactly this error.","Batch or cache DEK wraps where throughput matters; KMS throttling shows up here first on artifact-heavy flows."],"tags":["secrets","kms","artifacts","network","crypto"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}