semaphoreui/semaphore · error
secret must be valid json in key
Error message
secret must be valid json in key '%s'
What it means
After decrypting an access key secret, unmarshalAppropriateField parses the plaintext as JSON into the key's typed field. If decryption succeeded but the plaintext is not syntactically valid JSON (json.SyntaxError), the raw parse error is replaced by this clearer message naming the access key. The stored secret was valid ciphertext but its plaintext content is malformed.
Solutions
- Inspect and re-save the secret for the named key so its plaintext is valid JSON matching the key type (SshKey or LoginPassword shape).
- Re-create the access key via the API/UI so the serializer marshals a correctly structured JSON payload.
- If the plaintext is intentionally raw, change the key's type to one that stores plain values or fix the importing pipeline to JSON-encode values.
Example fix
// before: stored secret
my-secret-password
// after: valid JSON for a login-password key
{"login":"deploy","password":"my-secret-password"} Defensive patterns
Strategy: validation
Validate before calling
var v interface{}
if err := json.Unmarshal([]byte(plaintext), &v); err != nil {
// fix the stored secret before deserializing
} Try / catch
if err := svc.DeserializeSecret(key); err != nil && strings.Contains(err.Error(), "must be valid json") { /* re-save or recreate the key secret */ } Prevention
- Always write secrets through the API/serializer, never by hand-editing the store.
- Validate JSON with a schema matching the key type (SshKey/LoginPassword) on import.
- Trim and JSON-encode values when importing secrets from external systems.
When it happens
Trigger: DeserializeSecret (invoked via FillEnvironmentSecrets or GetTaskSurveySecrets) on a key whose decrypted secret is not parseable JSON — e.g. someone stored a plain string/password in a key that requires a JSON document.
Common situations: Secrets hand-edited in the secret store; values imported from another system as raw text; SSH/login-password keys overwritten with non-JSON values; encoding corruption after manual export/import.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
Related errors
AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07).
Data as JSON: /api/errors/1022b328816f2da5.
Report an issue: GitHub.
Appendix: source
Thrown at services/server/access_key_encryption_svc.go:158
func (s *accessKeyEncryptionServiceImpl) DeserializeSecret(key *db.AccessKey) error {
if key.ExpireAt != nil && tz.Now().After(*key.ExpireAt) {
return ErrAccessKeyExpired
}
d, _, err := s.getDeserializer(key)
if err != nil {
return err
}
ciphertext, err := d.DeserializeSecret(key)
if err != nil {
return err
}
err = unmarshalAppropriateField(key, []byte(ciphertext))
var syntaxError *json.SyntaxError
if errors.As(err, &syntaxError) {
err = fmt.Errorf("secret must be valid json in key '%s'", key.Name)
}
return err
}
func (s *accessKeyEncryptionServiceImpl) FillEnvironmentSecrets(env *db.Environment, deserializeSecret bool) error {
keys, err := s.environmentRepo.GetEnvironmentSecrets(env.ProjectID, env.ID)
if err != nil {
return err
}
for _, k := range keys {
var secretName string
var secretType db.EnvironmentSecretType
switch k.Owner {
case db.AccessKeyVariable:View on GitHub (pinned to 1774ccb71a)