hashicorp/nomad · error

tag %q already exists on a different version of job %q

Error message

tag %q already exists on a different version of job %q

What it means

Nomad job version tags (used with the job versions API) must be unique per job: a tag name may be set or updated on one version, but cannot be applied to a different version while another version already carries it. The state store walks all stored versions of the job and rejects the request if the tag name is bound to a version other than the target.

Source

Thrown at nomad/state/state_store.go:4935

	}

	return txn.Commit()
}

func (s *StateStore) updateJobVersionTagImpl(index uint64, namespace, jobID string, jobVersion uint64, tag *structs.JobVersionTag, txn *txn) error {
	// Note: could use JobByIDAndVersion to get the specific version we want here,
	// but then we'd have to make a second lookup to make sure we're not applying a duplicate tag name
	versions, err := s.JobVersionsByID(nil, namespace, jobID)
	if err != nil {
		return err
	}

	var job *structs.Job

	for _, version := range versions {
		// Allow for a tag to be updated (new description, for example) but otherwise don't allow a same-tagname to a different version.
		if version.VersionTag != nil && version.VersionTag.Name == tag.Name && version.Version != jobVersion {
			return fmt.Errorf("tag %q already exists on a different version of job %q", tag.Name, jobID)
		}
		if version.Version == jobVersion {
			job = version
		}
	}

	if job == nil {
		return fmt.Errorf("job %q version %d not found", jobID, jobVersion)
	}

	versionCopy := job.Copy()
	versionCopy.VersionTag = tag
	versionCopy.ModifyIndex = index

	latestJob, err := s.JobByID(nil, namespace, jobID)
	if err != nil {
		return err
	}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Unset the tag from the old version first (JobRevert endpoints or the unset tag API), then apply it to the new version
  2. Choose a unique tag name for the new version
  3. List job versions (GET /v1/job/:jobId/versions) to find which version currently holds the tag

Example fix

// before: POST tag {"Name":"stable"} on version 5 while version 3 already has "stable"
// after
1) DELETE /v1/job/myjob/versions/tag?tag=stable  (unset from version 3)
2) PUT /v1/job/myjob/versions/5/tag {"Name":"stable"}
Defensive patterns

Strategy: validation

Validate before calling

versions, _, _ := client.Jobs().Versions(jobID, false, nil)
for _, v := range versions {
  if v.VersionTag != nil && v.VersionTag.Name == newTag && v.Version != targetVersion {
    // unset from v.Version first
    client.Jobs().Revert... // or unsetTag(v.VersionTag.Name)
  }
}

Try / catch

err := applyTag(jobID, version, tag)
if err != nil && strings.Contains(err.Error(), "already exists on a different version") {
  unsetTag(jobID, tag.Name)
  err = applyTag(jobID, version, tag)
}

Prevention

When it happens

Trigger: Calling the JobVersionTag apply endpoint (PUT /v1/job/:jobId/versions/:version/tag or JobApplyTag RPC) with a tag.Name that exists on version N while targeting version M != N.

Common situations: Teams reusing a stable tag like "prod" or "canary" for a newly promoted version without first unsetting it from the old version; automation scripts that re-tag after a rollback.

Related errors


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