{"record":{"id":"2940433af8e36d2e","repo":"semaphoreui/semaphore","slug":"s-value-and-file-are-mutually-exclusive","errorCode":null,"errorMessage":"%s: 'value' and 'file' are mutually exclusive","messagePattern":"(.+?): 'value' and 'file' are mutually exclusive","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"util/config.go","lineNumber":1443,"sourceCode":"\n\t\tif strings.Contains(fieldName, \"password\") || strings.Contains(fieldName, \"secret\") || strings.Contains(fieldName, \"key\") {\n\t\t\tstrVal = \"***\"\n\t\t}\n\n\t\treturn fmt.Errorf(\n\t\t\t\"value of field '%v' is not valid: %v (Must match regex: '%v')\",\n\t\t\tfieldType.Name, strVal, rule,\n\t\t)\n\t}\n\n\treturn nil\n}\n\n// resolveKeySource returns the key material from a KeySource: the inline Value,\n// or the trimmed contents of File. Value and File are mutually exclusive.\nfunc resolveKeySource(ks KeySource, name string) (string, error) {\n\tif ks.Value != \"\" && ks.File != \"\" {\n\t\treturn \"\", fmt.Errorf(\"%s: 'value' and 'file' are mutually exclusive\", name)\n\t}\n\tif ks.File != \"\" {\n\t\tdata, err := os.ReadFile(ks.File)\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"%s: read key file %q: %w\", name, ks.File, err)\n\t\t}\n\t\treturn strings.TrimSpace(string(data)), nil\n\t}\n\treturn ks.Value, nil\n}\n\n// resolveEncryptionKeysFrom builds the runtime keyset from the keys-file config\n// plus the legacy flat fields, validating every resolved key. It does not mutate\n// global state. The flat fields are added to the registry (so new writes can stamp\n// them) and recorded as the legacy no-prefix decrypt keys.\nfunc resolveEncryptionKeysFrom(enc *EncryptionKeysConfig, flatAccess, flatOption string) (*keyset, error) {\n\tks := &keyset{\n\t\tbyID:         map[string]string{},","sourceCodeStart":1425,"sourceCodeEnd":1461,"githubUrl":"https://github.com/semaphoreui/semaphore/blob/1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa/util/config.go#L1425-L1461","documentation":"resolveKeySource returns this error when a KeySource (e.g. an entry under encryption_keys.keys) specifies both an inline Value and a File. The two sources are mutually exclusive by design, so the configuration is rejected rather than guessing which to use.","triggerScenarios":"Config with `encryption_keys.keys.<label>: {value: ..., file: ...}` both set; templating that merges defaults (file) with overrides (value) leaving both populated; hand-editing a keys file adding a value while file remains.","commonSituations":"Kubernetes-managed keys files combined with a manually added inline key; Helm values where both key/value and key/file got set; copy-pasting a config example and only partially removing the other field.","solutions":["Remove one of the two fields — keep `value:` for inline material or `file:` for a key path","Check Helm/config templates so defaults do not inject `file` when you set `value` (or vice versa)","Re-run config validation after editing to confirm only one source remains per key"],"exampleFix":"# before\nencryption_keys:\n  keys:\n    primary:\n      value: \"k1\"\n      file: /etc/semaphore/primary.key\n# after\nencryption_keys:\n  keys:\n    primary:\n      file: /etc/semaphore/primary.key","handlingStrategy":"validation","validationCode":"for label, ks := range cfg.Encryption.Keys {\n    if ks.Value != \"\" && ks.File != \"\" {\n        return fmt.Errorf(\"key %q sets both value and file\", label)\n    }\n}","typeGuard":null,"tryCatchPattern":"material, err := resolveKeySource(ks, name)\nif err != nil {\n    return fmt.Errorf(\"key source %s misconfigured: %w\", name, err)\n}","preventionTips":["Choose one key source per entry: inline value OR file, never both","Audit Helm/Ansible templates for defaults that inject the other field","Validate the encryption config before applying it to production"],"tags":["config","encryption","validation","semaphore"],"backgroundTag":"mutually-exclusive-options","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"}