{"record":{"id":"9925df96406a8a9e","repo":"hashicorp/nomad","slug":"secret-provider-cannot-be-empty","errorCode":null,"errorMessage":"secret provider cannot be empty","messagePattern":"secret provider cannot be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/structs.go","lineNumber":10639,"sourceCode":"}\n\nfunc (s *Secret) Validate() error {\n\tif s == nil {\n\t\treturn nil\n\t}\n\n\tvar mErr multierror.Error\n\n\tif s.Name == \"\" {\n\t\t_ = multierror.Append(&mErr, errors.New(\"secret name cannot be empty\"))\n\t}\n\n\tif !validSecretName.MatchString(s.Name) {\n\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"secret name must match regex %s\", validSecretName))\n\t}\n\n\tif s.Provider == \"\" {\n\t\t_ = multierror.Append(&mErr, errors.New(\"secret provider cannot be empty\"))\n\t}\n\n\tif s.Path == \"\" {\n\t\t_ = multierror.Append(&mErr, errors.New(\"secret path cannot be empty\"))\n\t}\n\n\tif s.Provider == \"nomad\" || s.Provider == \"vault\" {\n\t\tif len(s.Env) > 0 {\n\t\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"%s provider cannot use the env block\", s.Provider))\n\t\t}\n\t} else {\n\t\tif len(s.Config) > 0 {\n\t\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"custom plugin provider %s cannot use the config block\", s.Provider))\n\t\t}\n\t}\n\n\treturn mErr.ErrorOrNil()\n}","sourceCodeStart":10621,"sourceCodeEnd":10657,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/structs.go#L10621-L10657","documentation":"Secret struct validation requires a non-empty Provider field identifying which secrets backend (e.g. vault) owns the secret. When s.Provider is \"\" the validator appends this error to the multierror, rejecting the secret record. (The acl/acl.go USED AT references are unrelated context.)","triggerScenarios":"Submitting a secret creation request without the provider key, or constructing the Secret struct in Go with only Name/Path set and Provider left as the zero value before validation.","commonSituations":"Automation payloads copied from examples of a different API shape (provider nested elsewhere); version drift where the provider field was introduced later and older clients never send it; wrappers that only map name and path from user input.","solutions":["Set provider to a supported backend identifier (e.g. \"vault\") in the request payload or struct.","Update the client/SDK version so the provider field is populated per the current API schema.","Add client-side validation that fails fast when provider is missing before calling Nomad.","Check field-name mapping/serialization tags if unmarshaling from another format (e.g. 'backend' vs 'provider')."],"exampleFix":"// before (Go)\ns := &structs.Secret{Name: \"app-creds\", Path: \"kv/app\"}\n// after\ns := &structs.Secret{Name: \"app-creds\", Provider: \"vault\", Path: \"kv/app\"}","handlingStrategy":"validation","validationCode":"func validateSecretProvider(s *structs.Secret) error {\n\tif s.Provider == \"\" {\n\t\treturn fmt.Errorf(\"secret %q: provider is required (e.g. \\\"vault\\\")\", s.Name)\n\t}\n\treturn nil\n}","typeGuard":"func hasProvider(s *structs.Secret) bool { return s != nil && s.Provider != \"\" }","tryCatchPattern":"if err := secret.Validate(); err != nil {\n\tif strings.Contains(err.Error(), \"secret provider cannot be empty\") {\n\t\treturn fmt.Errorf(\"secret %q is missing its provider backend\", secret.Name)\n\t}\n\treturn err\n}","preventionTips":["Always set provider when creating secrets; keep a whitelist of supported backends in tooling.","Keep client SDKs updated so new required fields are sent.","Verify serialization field names match the API schema when mapping from other formats."],"tags":["nomad","secrets","validation","provider"],"backgroundTag":"missing-required-argument","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}