{"record":{"id":"1022b328816f2da5","repo":"semaphoreui/semaphore","slug":"secret-must-be-valid-json-in-key-s","errorCode":null,"errorMessage":"secret must be valid json in key '%s'","messagePattern":"secret must be valid json in key '(.+?)'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"services/server/access_key_encryption_svc.go","lineNumber":158,"sourceCode":"func (s *accessKeyEncryptionServiceImpl) DeserializeSecret(key *db.AccessKey) error {\n\tif key.ExpireAt != nil && tz.Now().After(*key.ExpireAt) {\n\t\treturn ErrAccessKeyExpired\n\t}\n\n\td, _, err := s.getDeserializer(key)\n\tif err != nil {\n\t\treturn err\n\t}\n\tciphertext, err := d.DeserializeSecret(key)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\terr = unmarshalAppropriateField(key, []byte(ciphertext))\n\n\tvar syntaxError *json.SyntaxError\n\tif errors.As(err, &syntaxError) {\n\t\terr = fmt.Errorf(\"secret must be valid json in key '%s'\", key.Name)\n\t}\n\n\treturn err\n}\n\nfunc (s *accessKeyEncryptionServiceImpl) FillEnvironmentSecrets(env *db.Environment, deserializeSecret bool) error {\n\tkeys, err := s.environmentRepo.GetEnvironmentSecrets(env.ProjectID, env.ID)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, k := range keys {\n\t\tvar secretName string\n\t\tvar secretType db.EnvironmentSecretType\n\n\t\tswitch k.Owner {\n\t\tcase db.AccessKeyVariable:","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/semaphoreui/semaphore/blob/1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa/services/server/access_key_encryption_svc.go#L140-L176","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: stored secret\nmy-secret-password\n// after: valid JSON for a login-password key\n{\"login\":\"deploy\",\"password\":\"my-secret-password\"}","handlingStrategy":"validation","validationCode":"var v interface{}\nif err := json.Unmarshal([]byte(plaintext), &v); err != nil {\n    // fix the stored secret before deserializing\n}","typeGuard":null,"tryCatchPattern":"if err := svc.DeserializeSecret(key); err != nil && strings.Contains(err.Error(), \"must be valid json\") { /* re-save or recreate the key secret */ }","preventionTips":["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."],"tags":["go","json","secrets","validation"],"backgroundTag":"invalid-json-response","analyzedSha":"1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa","analyzedAt":"2026-09-07T11:00:33.293Z","contentChangedAt":"2026-09-07T11:00:33.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}