semaphoreui/semaphore · error
jwt: re-encrypt signing key
Error message
jwt: re-encrypt signing key: %w
What it means
After successfully decrypting the JWT signing key plaintext, RekeyJWTSigningKey re-encrypts it with Config.EncryptOption, which uses the option keyring primary key (falling back to the access key). This error means the re-encryption step failed, typically because no encryption key material is configured.
Solutions
- Ensure the option keyring is configured (option key env/config, or a valid access key fallback) before running the rekey.
- Initialize Config/keyring before calling RekeyJWTSigningKey in custom tooling.
- Re-run the rekey once encryption keys are present - decryption already succeeded, so only the encrypt step needs a valid key.
Example fix
// before
util.RekeyJWTSigningKey(store, oldKey) // keyring not configured in this process
// after
util.LoadConfig() // sets up option/access keyrings from config/env
if !util.Config.HasOptionKey() {
return errors.New("configure option or access key before rekey")
}
util.RekeyJWTSigningKey(store, oldKey) Defensive patterns
Strategy: try-catch
Validate before calling
// ensure encryption is configured before rekey
if util.Config == nil || !util.Config.HasEncryptionKey() {
return errors.New("option/access encryption key not configured")
} Try / catch
if err := util.RekeyJWTSigningKey(store, oldKey); err != nil {
if strings.Contains(err.Error(), "re-encrypt signing key") {
return fmt.Errorf("no primary encryption key available for option keyring; set option key or access key fallback: %w", err)
}
return err
} Prevention
- Configure the option key (or ensure the access-key fallback exists) before key maintenance.
- Initialize config/keyring before invoking rekey in scripts or custom tooling.
- Keep the keyring setup identical across all environments touching the same database.
When it happens
Trigger: Calling RekeyJWTSigningKey when the option keyring has no primary key and no access-key fallback is available - e.g. missing/unset option-key or access-key configuration at rekey time.
Common situations: Option key env/config removed during a rotation; running the rekey command in an environment where Config encryption keys were never set up; keyring initialized after the rekey call.
Understand the failure class
Background: "missing required config value" errors: why libraries refuse to start when a configuration key is empty, unset, or blank — this error's family across 48 libraries.
Related errors
- : 'value' and 'file' are mutually exclusive
- : read key file
- encryption_keys.active.%s_key: no key labelled
- encryption_keys.active.%s_key_file
- encryption_keys.keys_folder
AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07).
Data as JSON: /api/errors/c0ddf3699254d1f4.
Report an issue: GitHub.
Appendix: source
Thrown at util/jwt.go:142
if err != nil {
return fmt.Errorf("read jwt signing key option: %w", err)
}
if stored == "" {
return nil
}
pemBytes, err := Config.DecryptOption(stored)
if err != nil && oldKey != "" {
_, ct, _ := parseEnvelope(stored)
pemBytes, err = DecryptAESGCM(ct, oldKey)
}
if err != nil {
return fmt.Errorf("jwt: decrypt signing key for rekey: %w", err)
}
reEncrypted, err := Config.EncryptOption(pemBytes)
if err != nil {
return fmt.Errorf("jwt: re-encrypt signing key: %w", err)
}
if reEncrypted == stored {
return nil
}
if err := store.SetOption(jwtSigningKeyOption, reEncrypted); err != nil {
return fmt.Errorf("jwt: persist re-encrypted signing key: %w", err)
}
return nil
}
// encryptJWTKey encrypts pemBytes using the option keyring primary key (which
// falls back to the access key when no separate option key is configured).
func encryptJWTKey(pemBytes []byte) (string, error) {
return Config.EncryptOption(pemBytes)
}
View on GitHub (pinned to 1774ccb71a)