{"record":{"id":"d5f2757d43df2c26","repo":"semaphoreui/semaphore","slug":"ciphertext-too-short","errorCode":null,"errorMessage":"ciphertext too short","messagePattern":"ciphertext too short","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"util/encryption.go","lineNumber":55,"sourceCode":"// DecryptAESGCM decrypts an AES-256-GCM ciphertext.\nfunc DecryptAESGCM(encodedCiphertext, encodedKey string) ([]byte, error) {\n\tciphertext, err := base64.StdEncoding.DecodeString(encodedCiphertext)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"base64 decode: %w\", err)\n\t}\n\n\tif encodedKey == \"\" {\n\t\treturn ciphertext, nil\n\t}\n\n\tgcm, err := newGCM(encodedKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tnonceSize := gcm.NonceSize()\n\tif len(ciphertext) < nonceSize {\n\t\treturn nil, errors.New(\"ciphertext too short\")\n\t}\n\n\tnonce, payload := ciphertext[:nonceSize], ciphertext[nonceSize:]\n\treturn gcm.Open(nil, nonce, payload, nil)\n}\n\nfunc newGCM(encodedKey string) (cipher.AEAD, error) {\n\tkeyBytes, err := base64.StdEncoding.DecodeString(encodedKey)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"decode encryption key: %w\", err)\n\t}\n\tblock, err := aes.NewCipher(keyBytes)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn cipher.NewGCM(block)\n}\n","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/semaphoreui/semaphore/blob/1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa/util/encryption.go#L37-L73","documentation":"Returned by DecryptAESGCM in util/encryption.go after base64 decoding succeeds but the decoded byte slice is shorter than the GCM nonce size. A valid AES-GCM payload always contains at least the nonce (typically 12 bytes) followed by the sealed ciphertext, so a shorter input means the value is truncated, empty, or not actually an AES-GCM ciphertext at all — a length guard that prevents an out-of-range slice before calling gcm.Open.","triggerScenarios":"Thrown at util/encryption.go:55 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Verify the stored value was produced by EncryptAESGCM and not truncated by a storage layer or manual copy","Re-encrypt and re-store the secret if the payload is unrecoverable","If the value is legacy plaintext, use the plaintext path (empty key) or migrate it to the encrypted format","Check that nothing strips bytes from the encoded string (e.g. trimming '=' padding before decode would already fail earlier)"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa","analyzedAt":"2026-09-07T11:00:33.293Z","contentChangedAt":"2026-09-07T11:00:33.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}