{"record":{"id":"172b346b29723217","repo":"JuliusBrussee/caveman","slug":"aes-cipher-w","errorCode":null,"errorMessage":"aes cipher: %w","messagePattern":"aes cipher: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/secretbox/secretbox.go","lineNumber":72,"sourceCode":"\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}\n\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) {","sourceCodeStart":54,"sourceCodeEnd":90,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/secretbox/secretbox.go#L54-L90","documentation":"After loading the local key, secretbox.Encrypt calls aes.NewCipher(keyBytes) to build an AES block cipher. Go's aes package returns an error only when the key length is not 16, 24, or 32 bytes (AES-128/192/256). This error therefore means the key material produced by loadKey() has an invalid length.","triggerScenarios":"Calling secretbox.Encrypt with useKMS() false (non-production) when CAVE_SECRETBOX_KEY (or whatever loadKey reads) decodes to a byte slice whose length is not 16/24/32 — e.g. a hex/base64 string truncated, double-encoded, or with stray whitespace/newline included in the decoded bytes.","commonSituations":"Pasting a key with a trailing newline or quotes into the env var; using a raw passphrase string of arbitrary length instead of a decoded 32-byte key; key file truncated by copy/paste or editor; switching between hex and base64 encodings without adjusting.","solutions":["Print len(keyBytes) at loadKey (or decode manually in a scratch program) and confirm it is exactly 16, 24, or 32 bytes.","Regenerate the key correctly, e.g. openssl rand -base64 32, and set the env var with no quoting/newline issues.","Ensure the decoding scheme in loadKey (hex vs base64) matches how the key was generated and stored.","Trim whitespace/newlines before decoding, or fix the stored value rather than trimming at runtime."],"exampleFix":"// before: raw passphrase used as key -> aes cipher: crypto/aes: invalid key size 11\nkey := os.Getenv(\"CAVE_SECRETBOX_KEY\")\n\n// after: exactly 32 random bytes, base64-decoded\nkeyB64 := strings.TrimSpace(os.Getenv(\"CAVE_SECRETBOX_KEY\"))\nkey, err := base64.StdEncoding.DecodeString(keyB64) // len == 32\nif err != nil || (len(key) != 16 && len(key) != 24 && len(key) != 32) {\n    log.Fatal(\"CAVE_SECRETBOX_KEY must decode to 16/24/32 bytes\")\n}","handlingStrategy":"validation","validationCode":"key, err := base64.StdEncoding.DecodeString(strings.TrimSpace(os.Getenv(\"CAVE_SECRETBOX_KEY\")))\nif err != nil || (len(key) != 16 && len(key) != 24 && len(key) != 32) {\n    return fmt.Errorf(\"CAVE_SECRETBOX_KEY must decode to 16/24/32 bytes, got %d\", len(key))\n}","typeGuard":null,"tryCatchPattern":"cipherText, err := secretbox.Encrypt(pt)\nif err != nil {\n    if strings.Contains(err.Error(), \"aes cipher\") {\n        return fmt.Errorf(\"misconfigured secretbox key: %w\", err)\n    }\n    return err\n}","preventionTips":["Generate keys with a fixed recipe (openssl rand -base64 32) and store them without added quoting or newlines.","Validate key length at startup, not at first use.","Never pass a human-chosen passphrase where a byte-exact AES key is required.","Keep generation and decoding encoding (hex vs base64) documented alongside the key."],"tags":["encryption","aes","key-management","configuration","go"],"backgroundTag":"invalid-argument-value","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"}