semaphoreui/semaphore · error

ciphertext too short

Error message

ciphertext too short

What it means

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.

Solutions

  1. Verify the stored value was produced by EncryptAESGCM and not truncated by a storage layer or manual copy
  2. Re-encrypt and re-store the secret if the payload is unrecoverable
  3. If the value is legacy plaintext, use the plaintext path (empty key) or migrate it to the encrypted format
  4. Check that nothing strips bytes from the encoded string (e.g. trimming '=' padding before decode would already fail earlier)
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at util/encryption.go:55 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07). Data as JSON: /api/errors/d5f2757d43df2c26. Report an issue: GitHub.

Appendix: source

Thrown at util/encryption.go:55

// DecryptAESGCM decrypts an AES-256-GCM ciphertext.
func DecryptAESGCM(encodedCiphertext, encodedKey string) ([]byte, error) {
	ciphertext, err := base64.StdEncoding.DecodeString(encodedCiphertext)
	if err != nil {
		return nil, fmt.Errorf("base64 decode: %w", err)
	}

	if encodedKey == "" {
		return ciphertext, nil
	}

	gcm, err := newGCM(encodedKey)
	if err != nil {
		return nil, err
	}

	nonceSize := gcm.NonceSize()
	if len(ciphertext) < nonceSize {
		return nil, errors.New("ciphertext too short")
	}

	nonce, payload := ciphertext[:nonceSize], ciphertext[nonceSize:]
	return gcm.Open(nil, nonce, payload, nil)
}

func newGCM(encodedKey string) (cipher.AEAD, error) {
	keyBytes, err := base64.StdEncoding.DecodeString(encodedKey)
	if err != nil {
		return nil, fmt.Errorf("decode encryption key: %w", err)
	}
	block, err := aes.NewCipher(keyBytes)
	if err != nil {
		return nil, err
	}
	return cipher.NewGCM(block)
}

View on GitHub (pinned to 1774ccb71a)