hashicorp/nomad · error

missing node IDs for client deregistration

Error message

missing node IDs for client deregistration

What it means

Guard in Node.BatchDeregister: the batch request contains no node IDs; at least one node ID is required to deregister clients.

Source

Thrown at nomad/node_endpoint.go:564

func (n *Node) BatchDeregister(args *structs.NodeBatchDeregisterRequest, reply *structs.NodeUpdateResponse) error {
	authErr := n.srv.Authenticate(n.ctx, args)
	if done, err := n.srv.forward("Node.BatchDeregister", args, args, reply); done {
		return err
	}
	n.srv.MeasureRPCRate("node", structs.RateMetricWrite, args)
	if authErr != nil {
		return structs.ErrPermissionDenied
	}
	defer metrics.MeasureSince([]string{"nomad", "client", "batch_deregister"}, time.Now())

	if aclObj, err := n.srv.ResolveACL(args); err != nil {
		return structs.ErrPermissionDenied
	} else if !aclObj.AllowNodeWrite() {
		return structs.ErrPermissionDenied
	}

	if len(args.NodeIDs) == 0 {
		return fmt.Errorf("missing node IDs for client deregistration")
	}

	return n.deregister(args, reply, func() (any, uint64, error) {
		return n.srv.raftApply(structs.NodeBatchDeregisterRequestType, args)
	})
}

// deregister takes a raftMessage closure, to support both Deregister and
// BatchDeregister. The caller should have already authorized the request.
func (n *Node) deregister(args *structs.NodeBatchDeregisterRequest,
	reply *structs.NodeUpdateResponse,
	raftApplyFn func() (any, uint64, error),
) error {
	// Look for the node
	snap, err := n.srv.fsm.State().Snapshot()
	if err != nil {
		return err
	}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Check len(NodeIDs) > 0 before issuing the RPC and no-op otherwise
  2. Fix upstream filtering that produced an empty list unexpectedly
  3. For a single node, use Node.Deregister instead
  4. Log and alert when a scheduled cleanup finds zero nodes rather than sending the request

Example fix

// before
client.Nodes().BatchDeregister(&api.NodeBatchDeregisterRequest{NodeIDs: ids})
// after
if len(ids) == 0 {
    return nil // nothing to deregister
}
client.Nodes().BatchDeregister(&api.NodeBatchDeregisterRequest{NodeIDs: ids})
Defensive patterns

Strategy: validation

Validate before calling

if len(nodeIDs) == 0 {
    return nil // nothing to do
}

Type guard

func hasIDs(ids []string) bool { return len(ids) > 0 }

Try / catch

err := client.Nodes().BatchDeregister(req, nil)
if err != nil && strings.Contains(err.Error(), "missing node IDs") {
    // empty batch: treat as no-op
}

Prevention

When it happens

Trigger: Calling BatchDeregister with a zero-length NodeIDs slice — e.g., a batch job whose input filter matched nothing and passed the empty result straight through.

Common situations: Bulk-cleanup automation where the query for dead nodes returned no rows; off-by-one filters; empty CSV input.

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/97f5289708b2ad4a. Report an issue: GitHub.