semaphoreui/semaphore · error
base64 decode
Error message
base64 decode: %w
What it means
Returned by DecryptAESGCM in util/encryption.go when the input ciphertext string is not valid standard base64. The function expects the AES-256-GCM ciphertext to be base64-encoded (nonce prefixed); failing to decode means the stored value is corrupted, truncated, was written in a different encoding (e.g. base64url without padding), or is plaintext from a legacy scheme. The %w wraps encoding/base64's CorruptInputError with the offending byte offset.
Solutions
- Check the wrapped CorruptInputError offset to locate the first invalid character in the stored value
- If the value came from a legacy or non-encrypted scheme, route it through the legacy plaintext path (an empty key returns the ciphertext unchanged) or migrate it
- Re-encrypt the secret with EncryptAESGCM to restore a well-formed base64 blob
- Verify the value was not mangled by storage or copy-paste (whitespace, URL-unsafe characters, missing padding)
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at util/encryption.go:41 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/3821b36907454b2d.
Report an issue: GitHub.
Appendix: source
Thrown at util/encryption.go:41
gcm, err := newGCM(encodedKey)
if err != nil {
return "", err
}
nonce := make([]byte, gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(gcm.Seal(nonce, nonce, plaintext, nil)), nil
}
// 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)View on GitHub (pinned to 1774ccb71a)