hashicorp/nomad · error

Current job has version %d; enforcing version %d

Error message

Current job has version %d; enforcing version %d

What it means

Revert supports an optional EnforcePriorVersion check: the caller can require that the job is still at an expected version before the rollback is applied, guarding against concurrent modifications. If the job's current version (cur.Version) differs from *args.EnforcePriorVersion, the RPC aborts with "Current job has version %d; enforcing version %d". It is an optimistic-concurrency guard, not a data corruption.

Source

Thrown at nomad/job_endpoint.go:610

	// Build the register request
	revJob := jobV.Copy()

	// Clear out the VersionTag to prevent tag duplication
	revJob.VersionTag = nil

	// Set the stable flag to false as this is functionally a new registration
	// and should handle deployment updates
	revJob.Stable = false

	reg := &structs.JobRegisterRequest{
		Job:          revJob,
		WriteRequest: args.WriteRequest,
	}

	// If the request is enforcing the existing version do a check.
	if args.EnforcePriorVersion != nil {
		if cur.Version != *args.EnforcePriorVersion {
			return fmt.Errorf("Current job has version %d; enforcing version %d", cur.Version, *args.EnforcePriorVersion)
		}

		reg.EnforceIndex = true
		reg.JobModifyIndex = cur.JobModifyIndex
	}

	// Register the version.
	allowedPermissions := []string{acl.NamespaceCapabilityRevertJob}
	return j.doRegister(aclObj, allowedPermissions, reg, reply)
}

// Stable is used to mark the job version as stable
func (j *Job) Stable(args *structs.JobStabilityRequest, reply *structs.JobStabilityResponse) error {
	authErr := j.srv.Authenticate(j.ctx, args)
	if done, err := j.srv.forward("Job.Stable", args, args, reply); done {
		return err
	}
	j.srv.MeasureRPCRate("job", structs.RateMetricWrite, args)

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Re-read the job's current version (`nomad job inspect` / Jobs().Info) and retry the revert with the fresh value
  2. If the new current version is acceptable, drop the EnforcePriorVersion guard (pass nil) and revert directly
  3. Coordinate/serialize deployments so concurrent writers don't race the revert
  4. Implement retry-with-refresh: on this error, re-fetch version, re-verify, and re-issue the revert

Example fix

// before (stale expected version)
enforce := uint64(4)
client.Jobs().Revert("webapp", 4, 7, &enforce, nil, nil)
// after (refresh before enforcing)
job, _, _ := client.Jobs().Info("webapp", nil)
enforce := *job.Version
client.Jobs().Revert("webapp", enforce, 7, &enforce, nil, nil)
Defensive patterns

Strategy: retry

Validate before calling

job, _, err := client.Jobs().Info(jobID, nil)
if err != nil { return err }
expected := *job.Version // refresh immediately before the enforced revert

Type guard

func versionMatches(job *api.Job, expected uint64) bool { return job != nil && job.Version != nil && *job.Version == expected }

Try / catch

for attempt := 0; attempt < 3; attempt++ {
    job, _, _ := client.Jobs().Info(jobID, nil)
    exp := *job.Version
    _, _, err := client.Jobs().Revert(jobID, exp, target, &exp, nil, nil)
    if err == nil { break }
    if !strings.Contains(err.Error(), "enforcing version") { return err }
    time.Sleep(time.Second) // version raced; refresh and retry
}

Prevention

When it happens

Trigger: Calling Job.Revert with a non-nil EnforcePriorVersion pointer whose value no longer matches the job's current registered version — e.g. the job was updated/reverted by someone else between when you read the version and issued the revert.

Common situations: CI/CD automation holding a stale expected version while another pipeline deploys; two operators reverting simultaneously; long-lived scripts built against an old snapshot of job state; retry logic replaying a revert with an outdated EnforcePriorVersion.

Related errors


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