{"record":{"id":"0d9ebfcdaaa5bef8","repo":"semaphoreui/semaphore","slug":"missing-secret","errorCode":null,"errorMessage":"missing secret","messagePattern":"missing secret","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db/Environment.go","lineNumber":75,"sourceCode":"\tSecretStorageID        *int    `db:\"secret_storage_id\" json:\"secret_storage_id,omitempty\" backup:\"-\"`\n\tSecretStorageKeyPrefix *string `db:\"secret_storage_key_prefix\" json:\"secret_storage_key_prefix,omitempty\"`\n\n\t// Sync fields are transfer-only; persisted in project__secret_sync.\n\tSyncEnabled      bool             `db:\"-\" json:\"sync_enabled\"`\n\tSyncInterval     int              `db:\"-\" json:\"sync_interval\"`\n\tLastSyncedAt     *time.Time       `db:\"-\" json:\"last_synced_at,omitempty\"`\n\tLastSyncFailedAt *time.Time       `db:\"-\" json:\"last_sync_failed_at,omitempty\"`\n\tSyncPaths        []SecretSyncPath `db:\"-\" json:\"sync_paths\"`\n}\n\nfunc (s *EnvironmentSecret) Validate() error {\n\n\tif s.Type == EnvironmentSecretVar || s.Type == EnvironmentSecretEnv {\n\t\treturn nil\n\t}\n\n\tif s.Secret == \"\" {\n\t\treturn errors.New(\"missing secret\")\n\t}\n\n\treturn errors.New(\"invalid environment secret type\")\n}\n\nfunc validateJSON(s string, mustValuesBeScalar bool) error {\n\tif s == \"\" {\n\t\treturn nil\n\t}\n\n\tvar data map[string]any\n\terr := json.Unmarshal([]byte(s), &data)\n\tif err != nil {\n\t\treturn errors.New(\"must be valid JSON\")\n\t}\n\n\tfor k, v := range data {\n\t\tif k == \"\" {","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/semaphoreui/semaphore/blob/1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa/db/Environment.go#L57-L93","documentation":"Environment.Validate on an EnvironmentSecret (EnvironmentVar/EnvironmentPassword type) requires the Secret field to be non-empty. Secret-typed environment entries must reference a stored secret value; an empty Secret means the entry carries no credential and would silently resolve to nothing at job runtime, so validation rejects it up front.","triggerScenarios":"Creating or updating an environment via the API (POST/PUT /api/environments) whose JSON env-vars payload contains an entry with type EnvironmentPassword (or a non-var/env type) and an empty/missing \"secret\" field, validated by Validate.","commonSituations":"Ansible/Terraform automation templating environment JSON where the secret variable is undefined and renders as an empty string; copying an environment definition between projects and dropping the secret value; users picking the wrong secret type when entering environment variables in the UI.","solutions":["Provide a non-empty \"secret\" value in the environment entry's JSON payload","If the entry holds a plain value, change its type to EnvironmentSecretVar (or EnvironmentSecretEnv) so the secret check is skipped","Templating tools: fail fast on empty variables instead of emitting empty strings into the env JSON"],"exampleFix":"// before\n{\"HOME_VAR\": {\"type\": \"password\", \"secret\": \"\"}}\n// after\n{\"HOME_VAR\": {\"type\": \"password\", \"secret\": \"vault-stored-password\"}}","handlingStrategy":"validation","validationCode":"if entry.Type != \"var\" && entry.Type != \"env\" && strings.TrimSpace(entry.Secret) == \"\" {\n\treturn errors.New(\"secret entry requires a non-empty secret value\")\n}","typeGuard":"func secretEntryIsValid(s db.EnvironmentSecret) bool {\n\tif s.Type == db.EnvironmentSecretVar || s.Type == db.EnvironmentSecretEnv { return true }\n\treturn s.Secret != \"\"\n}","tryCatchPattern":"if err := env.Validate(); err != nil {\n\tif strings.Contains(err.Error(), \"missing secret\") {\n\t\treturn fmt.Errorf(\"environment rejected: %w\", err)\n\t}\n\treturn err\n}","preventionTips":["Never template secrets with possibly-undefined variables; assert non-empty first","Use the var/env entry types for plain values, password type only with real secret values","Run Environment.Validate on composed payloads before calling the API"],"tags":["go","validation","environment","secrets"],"backgroundTag":"schema-validation-failed","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"}