semaphoreui/semaphore · error

decode encryption key

Error message

decode encryption key: %w

What it means

Returned by newGCM in util/encryption.go when the configured encryption key string cannot be base64-decoded. The keyring expects AES keys as base64-encoded raw bytes (32 bytes for AES-256); a decode failure means the key in config/storage was written as literal text, mangled, or encoded with a non-standard alphabet. The %w wraps base64.CorruptInputError with the offset of the first bad character.

Solutions

  1. Regenerate the key as 32 random bytes and store it base64-encoded (e.g. openssl rand -base64 32)
  2. Check the wrapped CorruptInputError offset to find invalid characters in the current key string
  3. Ensure the key was not passed through a layer that altered it (whitespace trimming, URL-encoding, quote stripping)
  4. If secrets were encrypted under the malformed key they are unreadable — re-encrypt them after fixing the key
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at util/encryption.go:65 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/6607bf8feb1e2e8e. Report an issue: GitHub.

Appendix: source

Thrown at util/encryption.go:65

	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)
}

func GeneratePrivateKey(privateKeyFile io.Writer) (publicKey string, err error) {
	// 1. Generate RSA Private Key (2048 bits)
	privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		return
	}

	// 2. Encode the private key to PKCS#1 ASN.1 PEM
	privateKeyBytes := x509.MarshalPKCS1PrivateKey(privateKey)
	privateKeyPem := &pem.Block{

View on GitHub (pinned to 1774ccb71a)