hashicorp/nomad · error

can't run terminal deployment

Error message

can't run terminal deployment

What it means

Sentinel ErrDeploymentTerminalNoRun returned by the deployment endpoint when running (e.g. on-demand upgrades) is attempted on a deployment that is not active. Terminal deployments accept no further lifecycle actions.

Solutions

  1. Confirm the deployment is active before calling Run.
  2. Skip Run for terminal deployments in automation.
  3. If the deployment failed, start a fresh deployment instead of unblocking.

Example fix

// before
client.Deployments().UnblockJobRun(deployID, nil)
// after
d, _ := client.Deployments().Info(deployID, nil)
if d.Status == "running" { client.Deployments().UnblockJobRun(deployID, nil) }
Defensive patterns

Strategy: type-guard

Validate before calling

d, err := client.Deployments().Info(deployID, nil)
if err != nil { return err }
if d.Status != "running" { return nil } // run not applicable

Type guard

func deploymentActive(d *api.Deployment) bool {
    return d.Status == "running" || d.Status == "pending"
}

Try / catch

err := client.Deployments().UnblockJobRun(deployID, nil)
if err != nil && strings.Contains(err.Error(), "terminal deployment") {
    return checkDeploymentOutcome(deployID)
}

Prevention

When it happens

Trigger: Calling Deployment.Run (or 'nomad deployment unblock -run') on a completed, failed, or cancelled deployment.

Common situations: Operators resuming multi-region/federated deployments whose remote portion already terminated; automation replaying Run commands.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at nomad/structs/errors.go:84

	ErrIncompatibleFiltering      = errors.New(errIncompatibleFiltering)
	ErrMalformedChooseParameter   = errors.New(errMalformedChooseParameter)

	// ErrResultPaginatorCreation is returned by list RPC handlers when the
	// result paginator cannot be built, for example when the server cannot
	// evaluate a requested filter expression. api.ResultPaginatorErrorContent
	// duplicates its message so the CLI can match it without importing structs.
	// Keep the two in sync.
	ErrResultPaginatorCreation = errors.New(errResultPaginatorCreation)

	ErrUnknownNode = errors.New(ErrUnknownNodePrefix)

	ErrDeploymentTerminalNoCancel    = errors.New(errDeploymentTerminalNoCancel)
	ErrDeploymentTerminalNoFail      = errors.New(errDeploymentTerminalNoFail)
	ErrDeploymentTerminalNoPause     = errors.New(errDeploymentTerminalNoPause)
	ErrDeploymentTerminalNoPromote   = errors.New(errDeploymentTerminalNoPromote)
	ErrDeploymentTerminalNoResume    = errors.New(errDeploymentTerminalNoResume)
	ErrDeploymentTerminalNoUnblock   = errors.New(errDeploymentTerminalNoUnblock)
	ErrDeploymentTerminalNoRun       = errors.New(errDeploymentTerminalNoRun)
	ErrDeploymentTerminalNoSetHealth = errors.New(errDeploymentTerminalNoSetHealth)
	ErrDeploymentRunningNoUnblock    = errors.New(errDeploymentRunningNoUnblock)

	ErrCSIClientRPCIgnorable  = errors.New("CSI client error (ignorable)")
	ErrCSIClientRPCRetryable  = errors.New("CSI client error (retryable)")
	ErrCSIVolumeMaxClaims     = errors.New("volume max claims reached")
	ErrCSIVolumeUnschedulable = errors.New("volume is currently unschedulable")
	ErrCSIPluginInUse         = errors.New("plugin in use")
)

// IsErrNoLeader returns whether the error is due to there being no leader.
func IsErrNoLeader(err error) bool {
	return err != nil && strings.Contains(err.Error(), errNoLeader)
}

// IsErrNoRegionPath returns whether the error is due to there being no path to
// the given region.
func IsErrNoRegionPath(err error) bool {

View on GitHub (pinned to 482b49bf1a)