hashicorp/nomad · error

must specify at least one healthy/unhealthy allocation ID

Error message

must specify at least one healthy/unhealthy allocation ID

What it means

Deployment.SetAllocHealth requires at least one allocation ID in HealthyAllocationIDs or UnhealthyAllocationIDs. Recording health for zero allocations is meaningless, so the server rejects it after the ID check.

Source

Thrown at nomad/deployment_endpoint.go:422

// deployment.
func (d *Deployment) SetAllocHealth(args *structs.DeploymentAllocHealthRequest, reply *structs.DeploymentUpdateResponse) error {
	authErr := d.srv.Authenticate(d.ctx, args)
	if done, err := d.srv.forward("Deployment.SetAllocHealth", args, args, reply); done {
		return err
	}
	d.srv.MeasureRPCRate("deployment", structs.RateMetricWrite, args)
	if authErr != nil {
		return structs.ErrPermissionDenied
	}
	defer metrics.MeasureSince([]string{"nomad", "deployment", "set_alloc_health"}, time.Now())

	// Validate the arguments
	if args.DeploymentID == "" {
		return fmt.Errorf("missing deployment ID")
	}

	if len(args.HealthyAllocationIDs)+len(args.UnhealthyAllocationIDs) == 0 {
		return fmt.Errorf("must specify at least one healthy/unhealthy allocation ID")
	}

	// Lookup the deployment
	snap, err := d.srv.fsm.State().Snapshot()
	if err != nil {
		return err
	}

	ws := memdb.NewWatchSet()
	deploy, err := snap.DeploymentByID(ws, args.DeploymentID)
	if err != nil {
		return err
	}
	if deploy == nil {
		return fmt.Errorf("deployment not found")
	}

	// Check namespace submit-job permissions

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Fetch the deployment's allocations (`nomad deployment status <id>`) and pass at least one alloc ID per the API contract
  2. Gate the call: skip SetAllocHealth when there are no allocs to mark yet
  3. If using `nomad deployment promote`/`unblock` instead, note those don't need alloc IDs

Example fix

// before
if len(healthy) == 0 { /* still called anyway */ }
callSetAllocHealth(deployID, healthy, nil)
// after
if len(healthy) == 0 && len(unhealthy) == 0 {
    return nil // nothing to mark; wait for allocs
}
callSetAllocHealth(deployID, healthy, unhealthy)
Defensive patterns

Strategy: validation

Validate before calling

allocs, _, _ := client.Deployments().Allocations(deployID, nil)
if len(allocs) == 0 { return nil } // nothing to mark yet
req := &api.DeploymentAllocHealthRequest{
    DeploymentID: deployID,
    HealthyAllocationIDs: healthyIDs(allocs),
    UnhealthyAllocationIDs: unhealthyIDs(allocs),
}

Type guard

function hasAllocIDs(r) { return (r.HealthyAllocationIDs?.length ?? 0) > 0 || (r.UnhealthyAllocationIDs?.length ?? 0) > 0; }

Try / catch

try {
  await setAllocHealth(req);
} catch (e) {
  if (String(e).includes("must specify at least one")) {
    // allocs not placed yet — poll deployment status and retry later
    return pollAndRetry(deployID);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling SetAllocHealth with a valid DeploymentID but both alloc ID slices empty — e.g. filtering allocations and matching none, or wiring only healthy IDs when there are none and no unhealthy IDs either.

Common situations: Canary health automation that selects allocs by status/name and gets an empty list (deploy just started, no allocs placed yet); script parsing `nomad job allocs` output incorrectly.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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