semaphoreui/semaphore · error
source storage key is required
Error message
source storage key is required
What it means
LocalAccessKeyDeserializer.deserialize handles keys that were originally stored in an external source (env var or vault) before being imported locally. If SourceStorageType is set, SourceStorageKey must also be set — it names where the secret lives. A nil SourceStorageKey with a non-nil SourceStorageType is an inconsistent record and yields this error.
Solutions
- Populate key.SourceStorageKey with the original env var name / vault path before deserializing.
- If the secret now lives locally, clear SourceStorageType so the normal local-decryption path is used.
- Re-run the migration tooling that converts source-stored secrets into local encrypted secrets.
Example fix
// before key.SourceStorageType = &t // set, but no source key // after src := "MY_SECRET_ENV_VAR" key.SourceStorageType = &t key.SourceStorageKey = &src
Defensive patterns
Strategy: validation
Validate before calling
if key.SourceStorageType != nil && key.SourceStorageKey == nil {
// inconsistent record: set SourceStorageKey or clear SourceStorageType
} Try / catch
if _, err := svc.DeserializeSecret(key); err != nil && strings.Contains(err.Error(), "source storage key is required") { /* repair the record before retry */ } Prevention
- Run migrations atomically so SourceStorageType and SourceStorageKey are written together.
- Include source-storage columns in DB backups/restores.
- Verify imported keys have consistent source-storage metadata.
When it happens
Trigger: DeserializeSecret/DeserializeSecret2 on an AccessKey with SourceStorageType != nil but SourceStorageKey == nil (e.g. partially migrated key, DB row missing the source key column).
Common situations: Interrupted migrations from env-var/vault-backed secrets; backup/restore dropping the source-storage-key column; hand-edited access_keys rows.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- missing secret
- secret must be valid json in key
- invalid ssh key
- invalid password key
- invalid access key type
AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07).
Data as JSON: /api/errors/cf1126582de13075.
Report an issue: GitHub.
Appendix: source
Thrown at services/server/access_key_serializer_local.go:100
return util.Config.DecryptAccessSecret(stored)
})
}
// DeserializeSecret2 decrypts using a single explicit key (stripping any key-id
// prefix). It is kept for the rekey `--old-key` path and for tests.
func (d *LocalAccessKeyDeserializer) DeserializeSecret2(key *db.AccessKey, encryptionString string) (res string, err error) {
return d.deserialize(key, func(stored string) ([]byte, error) {
return util.Config.DecryptAccessSecretWithKey(stored, encryptionString)
})
}
// deserialize handles the source-storage / legacy / nil cases, then decrypts the
// stored ciphertext with the supplied decryptor (keyset by id, or an explicit key).
func (d *LocalAccessKeyDeserializer) deserialize(key *db.AccessKey, decrypt func(string) ([]byte, error)) (res string, err error) {
if key.SourceStorageType != nil {
if key.SourceStorageKey == nil {
return "", fmt.Errorf("source storage key is required")
}
switch *key.SourceStorageType {
case db.AccessKeySourceStorageEnv:
res = os.Getenv(*key.SourceStorageKey)
return
case db.AccessKeySourceStorageFile:
filePath := filepath.Clean(*key.SourceStorageKey)
if !filepath.IsAbs(filePath) {
err = common_errors.NewUserErrorS("file path must be absolute")
return
}
for _, segment := range strings.Split(filepath.ToSlash(*key.SourceStorageKey), "/") {
if segment == ".." {
err = common_errors.NewUserErrorS("file path must not contain traversal segments")
returnView on GitHub (pinned to 1774ccb71a)