hashicorp/nomad · error

job %q in namespace %q doesn't exist to be deregistered

Error message

job %q in namespace %q doesn't exist to be deregistered

What it means

When deregistering a job, the FSM looks up the current job to mark it stopped. If the job does not exist at all (JobByIDTxn returns nil, nil), it returns this explicit 'doesn't exist to be deregistered' error. The deregistration apply fails because there is nothing to stop.

Source

Thrown at nomad/fsm.go:917

			return fmt.Errorf("DeleteJob failed: %w", err)
		}

		// We always delete from the periodic launch table because it is possible that
		// the job was updated to be non-periodic, thus checking if it is periodic
		// doesn't ensure we clean it up properly.
		n.state.DeletePeriodicLaunchTxn(index, namespace, jobID, tx)
		return nil
	}

	// Get the current job and mark it as stopped and re-insert it.
	ws := memdb.NewWatchSet()
	current, err := n.state.JobByIDTxn(ws, namespace, jobID, tx)
	if err != nil {
		return fmt.Errorf("JobByID lookup failed: %w", err)
	}

	if current == nil {
		return fmt.Errorf("job %q in namespace %q doesn't exist to be deregistered", jobID, namespace)
	}

	stopped := current.Copy()
	stopped.Stop = true
	if submitTime != 0 {
		stopped.SubmitTime = submitTime
	}

	// Disable scaling policies to avoid monitoring stopped jobs
	scalingPolicies := stopped.GetScalingPolicies()
	for _, policy := range scalingPolicies {
		policy.Enabled = false
	}

	if err := n.state.UpsertJobTxn(index, nil, stopped, tx); err != nil {
		return fmt.Errorf("UpsertJob failed: %w", err)
	}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Verify the job ID and namespace: `nomad job status -namespace <ns> <jobID>`; correct the namespace if wrong.
  2. Treat this as idempotent success if the desired end state is 'job gone' — the job is already deregistered.
  3. Check whether the job was already purged earlier; re-register the job if it should exist.
  4. Fix automation/tooling to only deregister jobs it owns or to check existence first.

Example fix

// before
_, _, err := client.Jobs().Deregister(jobID, false, nil)
// after
q := &nomad.QueryOptions{Namespace: ns}
if _, _, err := client.Jobs().Info(jobID, q); err == nil {
    _, _, err = client.Jobs().Deregister(jobID, false, q)
}
Defensive patterns

Strategy: validation

Validate before calling

q := &nomad.QueryOptions{Namespace: ns}
if _, _, err := client.Jobs().Info(jobID, q); err != nil {
    // job does not exist in this namespace; nothing to deregister
    return nil
}

Type guard

func jobExists(client *nomad.Client, ns, jobID string) bool {
    _, _, err := client.Jobs().Info(jobID, &nomad.QueryOptions{Namespace: ns})
    return err == nil
}

Try / catch

err := deregister(jobID)
if err != nil && strings.Contains(err.Error(), "doesn't exist to be deregistered") {
    // treat as idempotent success
}

Prevention

When it happens

Trigger: handleJobDeregister receives a deregistration for a namespace/jobID with no job record — e.g. duplicate deregistration, deregister sent to the wrong namespace, or the job was already purged.

Common situations: Double-submitting a job stop via API/UI, deregistering with a wrong --namespace flag, automation that lost track of whether the job was already removed, and jobs already purged with purge=true earlier.

Related errors


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