{"record":{"id":"99667792e915a8ab","repo":"hashicorp/nomad","slug":"required-and-optional-meta-keys-should-be-disjoint","errorCode":null,"errorMessage":"Required and optional meta keys should be disjoint. Following keys exist in both: %v","messagePattern":"Required and optional meta keys should be disjoint\\. Following keys exist in both: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/structs/structs.go","lineNumber":6095,"sourceCode":"\t// MetaRequired is metadata keys that must be specified by the dispatcher\n\tMetaRequired []string\n\n\t// MetaOptional is metadata keys that may be specified by the dispatcher\n\tMetaOptional []string\n}\n\nfunc (d *ParameterizedJobConfig) Validate() error {\n\tvar mErr multierror.Error\n\tswitch d.Payload {\n\tcase DispatchPayloadOptional, DispatchPayloadRequired, DispatchPayloadForbidden:\n\tdefault:\n\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"Unknown payload requirement: %q\", d.Payload))\n\t}\n\n\t// Check that the meta configurations are disjoint sets\n\tdisjoint, offending := helper.IsDisjoint(d.MetaRequired, d.MetaOptional)\n\tif !disjoint {\n\t\t_ = multierror.Append(&mErr, fmt.Errorf(\"Required and optional meta keys should be disjoint. Following keys exist in both: %v\", offending))\n\t}\n\n\treturn mErr.ErrorOrNil()\n}\n\nfunc (d *ParameterizedJobConfig) Canonicalize() {\n\tif d.Payload == \"\" {\n\t\td.Payload = DispatchPayloadOptional\n\t}\n}\n\nfunc (d *ParameterizedJobConfig) Copy() *ParameterizedJobConfig {\n\tif d == nil {\n\t\treturn nil\n\t}\n\tnd := new(ParameterizedJobConfig)\n\t*nd = *d\n\tnd.MetaOptional = slices.Clone(nd.MetaOptional)","sourceCodeStart":6077,"sourceCodeEnd":6113,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/structs/structs.go#L6077-L6113","documentation":"ParameterizedJobConfig.Validate requires that MetaRequired and MetaOptional are disjoint sets, verified with helper.IsDisjoint. If any key appears in both lists, this error lists the offending keys, since a dispatch meta key cannot simultaneously be mandatory and optional.","triggerScenarios":"Submitting a parameterized job whose parameter_meta block declares the same key in both required_keys and optional_keys (e.g. meta_required = [\"env\"], meta_optional = [\"env\"]).","commonSituations":"Accumulating meta keys over time without deduplicating; merging two job templates where one lists a key as required and the other as optional; copy-paste of key lists.","solutions":["Remove the duplicated key(s) named in the error from either meta_required or meta_optional.","Decide per key whether dispatch callers must supply it (required) or may supply it (optional).","Add a config lint that asserts the two key lists are disjoint before submission.","Use `nomad job validate` to verify the corrected job."],"exampleFix":"// before\nparameterized_job {\n  meta_required  = [\"env\"]\n  meta_optional  = [\"env\", \"region\"]\n}\n// after\nparameterized_job {\n  meta_required  = [\"env\"]\n  meta_optional  = [\"region\"]\n}","handlingStrategy":"validation","validationCode":"func metaKeysDisjoint(req, opt []string) bool {\n    set := make(map[string]struct{}, len(req))\n    for _, k := range req {\n        set[k] = struct{}{}\n    }\n    for _, k := range opt {\n        if _, ok := set[k]; ok {\n            return false\n        }\n    }\n    return true\n}\n// call before submitting: if !metaKeysDisjoint(pj.MetaRequired, pj.MetaOptional) { ... }","typeGuard":null,"tryCatchPattern":"if err := job.ParameterizedJob.Validate(); err != nil {\n    if strings.Contains(err.Error(), \"should be disjoint\") {\n        // dedupe the offending keys reported in the message\n    }\n}","preventionTips":["Keep a single source of truth for meta key classification.","Deduplicate key lists when merging job templates.","Add a schema lint asserting disjointness of required/optional keys.","Decide required-vs-optional per key once, at job authoring time."],"tags":["nomad","validation","dispatch","configuration"],"backgroundTag":"config-conflict","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"}