hashicorp/nomad · error

job ID must be set to set group

Error message

job ID must be set to set group

What it means

Within the JobACL validation block, Validate requires JobID to be set whenever Group is set — a group restriction is only meaningful relative to a specific job. The policy is rejected with 'job ID must be set to set group' and folded into the multierror.

Source

Thrown at nomad/structs/acl.go:388

	if !ValidPolicyName.MatchString(a.Name) {
		err := fmt.Errorf("invalid name '%s'", a.Name)
		mErr.Errors = append(mErr.Errors, err)
	}
	if _, err := acl.Parse(a.Rules, acl.PolicyParseStrict); err != nil {
		err = fmt.Errorf("failed to parse rules: %v", err)
		mErr.Errors = append(mErr.Errors, err)
	}
	if len(a.Description) > maxPolicyDescriptionLength {
		err := fmt.Errorf("description longer than %d", maxPolicyDescriptionLength)
		mErr.Errors = append(mErr.Errors, err)
	}
	if a.JobACL != nil {
		if a.JobACL.JobID != "" && a.JobACL.Namespace == "" {
			err := fmt.Errorf("namespace must be set to set job ID")
			mErr.Errors = append(mErr.Errors, err)
		}
		if a.JobACL.Group != "" && a.JobACL.JobID == "" {
			err := fmt.Errorf("job ID must be set to set group")
			mErr.Errors = append(mErr.Errors, err)
		}
		if a.JobACL.Task != "" && a.JobACL.Group == "" {
			err := fmt.Errorf("group must be set to set task")
			mErr.Errors = append(mErr.Errors, err)
		}
	}

	return mErr.ErrorOrNil()
}

// ACLPolicyListStub is used to for listing ACL policies
type ACLPolicyListStub struct {
	Name        string
	Description string
	JobACL      *JobACL
	Hash        []byte
	CreateIndex uint64

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Add the parent JobID to the JobACL block alongside Group.
  2. If cluster/group-wide access was intended, remove JobACL and grant the capability at namespace scope instead.
  3. Validate the policy JSON locally before submission to catch partial JobACL objects.

Example fix

// before
"JobACL": {"Namespace": "default", "Group": "web"}
// after
"JobACL": {"Namespace": "default", "JobID": "webapp", "Group": "web"}
Defensive patterns

Strategy: validation

Validate before calling

func validateGroupACL(j *JobACL) error {
    if j != nil && j.Group != "" && j.JobID == "" {
        return errors.New("job ID must be set when Group is set")
    }
    return nil
}

Type guard

func groupScopedCorrectly(j *structs.JobACL) bool {
    return j == nil || j.Group == "" || j.JobID != ""
}

Try / catch

if err := policy.Validate(); err != nil {
    if strings.Contains(err.Error(), "job ID must be set to set group") {
        return fmt.Errorf("JobACL.Group requires JobID: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Submitting an ACL policy with JobACL.Group non-empty but JobACL.JobID empty, e.g. JSON {"JobACL":{"Namespace":"default","Group":"web"}} or templated apply where the job variable was empty.

Common situations: Authors assuming group-scoped rules work cluster-wide like namespace rules; copy-paste of a namespace-only policy then adding Group without JobID; automation that fills group from a task name but not the job.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/23ce5b3425ac1d29. Report an issue: GitHub.