{"record":{"id":"4e5a367acd3a63c2","repo":"JuliusBrussee/caveman","slug":"kms-plaintext-is-empty","errorCode":null,"errorMessage":"kms: plaintext is empty","messagePattern":"kms: plaintext is empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/kms/kms.go","lineNumber":177,"sourceCode":"\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn client.Encrypt(ctx, plaintext)\n}\n\n// EncryptPayload wraps an artifact data key with the dedicated payload KEK.\nfunc EncryptPayload(ctx context.Context, plaintext []byte) ([]byte, error) {\n\tclient, err := FromPayloadEnvironment()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn client.Encrypt(ctx, plaintext)\n}\n\n// Encrypt delegates encryption to key manager.\nfunc (c *Client) Encrypt(ctx context.Context, plaintext []byte) ([]byte, error) {\n\tif len(plaintext) == 0 {\n\t\treturn nil, errors.New(\"kms: plaintext is empty\")\n\t}\n\tif len(plaintext) > maxPlaintextBytes {\n\t\treturn nil, fmt.Errorf(\"kms: plaintext exceeds %d bytes\", maxPlaintextBytes)\n\t}\n\tvar response struct {\n\t\tKeyID      string `json:\"key_id\"`\n\t\tCiphertext string `json:\"ciphertext\"`\n\t}\n\tif err := c.call(ctx, c.region, c.keyID, \"encrypt\", map[string]string{\n\t\t\"plaintext\": base64.StdEncoding.EncodeToString(plaintext),\n\t}, &response); err != nil {\n\t\treturn nil, err\n\t}\n\tif response.KeyID != c.keyID || strings.TrimSpace(response.Ciphertext) == \"\" {\n\t\treturn nil, errors.New(\"kms: invalid encrypt response\")\n\t}\n\tenvelope, err := json.Marshal(Envelope{Provider: c.provider, Region: c.region, KeyID: response.KeyID, Ciphertext: response.Ciphertext})\n\tif err != nil {","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/kms/kms.go#L159-L195","documentation":"Client.Encrypt rejects an empty plaintext slice before any network call, because the Scaleway KMS API has no meaningful zero-byte encryption and the library guards its size envelope invariants. The caller must supply at least one byte.","triggerScenarios":"Calling Encrypt(ctx, nil) or Encrypt(ctx, []byte{}) — e.g. encrypting an empty file, an empty string converted with []byte(\"\"), or data that was trimmed to nothing upstream.","commonSituations":"Empty uploads or fields processed by a pipeline that does not check length, refactoring that changed a value from a string to bytes without guarding empty, test scaffolding passing zero values.","solutions":["Check len(plaintext) > 0 before calling Encrypt and return a domain-specific empty-input error otherwise","Handle the empty-input case explicitly at a higher level (skip encryption, store empty marker)","If empty values are legitimate, encode them as a sentinel (e.g. encrypt a versioned wrapper) rather than raw zero bytes"],"exampleFix":"// before\nout, err := client.Encrypt(ctx, []byte(s)) // panics-free but errors when s==\"\"\n// after\nif s == \"\" { return nil, errors.New(\"nothing to encrypt\") }\nout, err := client.Encrypt(ctx, []byte(s))","handlingStrategy":"validation","validationCode":"if len(plaintext) == 0 {\n\treturn errors.New(\"nothing to encrypt\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Check length of byte payloads before encryption calls","Handle empty inputs (empty files/fields) explicitly in business logic","Convert strings to bytes only after confirming non-empty"],"tags":["kms","validation","empty-input"],"backgroundTag":"empty-required-field","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"}