semaphoreui/semaphore · critical
access_key_encryption has invalid decoded length
Error message
access_key_encryption has invalid decoded length %d bytes; AES requires 16, 24, or 32 bytes (use `openssl rand -base64 32` to generate a valid key)
What it means
The access_key_encryption value decoded from base64 successfully, but its byte length is not 16, 24, or 32 — the only key sizes AES accepts (AES-128/192/256). The error reports the actual decoded length and suggests generating a key with `openssl rand -base64 32`. It fails startup via panic in validateConfig.
Solutions
- Generate a fresh valid key with `openssl rand -base64 32` and replace the configured value.
- Pad/truncate key material to exactly 16, 24, or 32 decoded bytes — verify with `base64 -d <value> | wc -c`.
- Do not double-encode: if the value already looks base64, decode it, check its length, and use the correct single encoding.
- If old data was encrypted with a 16/24/32-byte key, recover that exact key; you cannot change lengths without re-encrypting existing access keys.
Example fix
// before: 20-byte decoded key export SEMAPHORE_ACCESS_KEY_ENCRYPTION=$(openssl rand -base64 15) # decodes to 15 bytes // after export SEMAPHORE_ACCESS_KEY_ENCRYPTION=$(openssl rand -base64 32) # decodes to 32 bytes
Defensive patterns
Strategy: validation
Validate before calling
raw, err := base64.StdEncoding.DecodeString(value)
if err != nil {
log.Fatal("not base64")
}
switch len(raw) {
case 16, 24, 32:
// ok
default:
log.Fatalf("decoded %d bytes; need 16/24/32", len(raw))
} Try / catch
defer func() {
if r := recover(); r != nil {
log.Fatalf("invalid AES key length: %v", r)
}
}() Prevention
- Use `openssl rand -base64 32` verbatim — never truncate its output.
- Remember `openssl rand -base64 N` decodes to ~N bytes; N must be 16, 24, or 32.
- Never double-encode; if a value already looks like base64, decode and inspect before reuse.
When it happens
Trigger: validateAccessKeyEncryption() decodes a non-empty access_key_encryption/option_encryption value and the switch on len(encryption) hits the default case (decoded length not in {16,24,32}).
Common situations: User generated `openssl rand -base64 16` thinking 16 chars, but truncating a 32-byte base64 string yields e.g. 20 decoded bytes; base64 of a short password (e.g. 8-10 bytes); re-encoding an already-base64 string (double encoding changes length); hand-typed key of arbitrary length.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- access_key_encryption must be a valid base64 string
- encryption_keys.keys_folder: key
- value of field ' ' is not valid: (Must match regex: ' ')
- : 'value' and 'file' are mutually exclusive
- OIDC sign-in failed: invalid state. Try signing in again.
AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07).
Data as JSON: /api/errors/29dc0eb882d38827.
Report an issue: GitHub.
Appendix: source
Thrown at util/config.go:1742
return &enc, nil
}
func validateAccessKeyEncryption(key string) error {
if key == "" {
return nil
}
encryption, err := base64.StdEncoding.DecodeString(key)
if err != nil {
return fmt.Errorf("access_key_encryption must be a valid base64 string: %w", err)
}
switch len(encryption) {
case 16, 24, 32:
return nil
default:
return fmt.Errorf(
"access_key_encryption has invalid decoded length %d bytes; AES requires 16, 24, or 32 bytes (use `openssl rand -base64 32` to generate a valid key)",
len(encryption),
)
}
}
func validateConfig() {
err := validate(Config)
if err != nil {
panic(err)
}
if err := validateAccessKeyEncryption(Config.AccessKeyEncryption); err != nil {
panic(err)
}
if err := validateAccessKeyEncryption(Config.OptionEncryption); err != nil {
panic(err)
}View on GitHub (pinned to 1774ccb71a)