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
- Check len(NodeIDs) > 0 before issuing the RPC and no-op otherwise
- Fix upstream filtering that produced an empty list unexpectedly
- For a single node, use Node.Deregister instead
- 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
- Guard batch jobs for empty result sets before issuing RPCs
- Log when a cleanup pass finds zero nodes
- Prefer no-op over empty batch requests
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
- given no jobs to deregister
- missing node ID for client deregistration
- missing secret ID
- namespace cannot contain template delimiters or parenthesis
- wait config is nil or empty
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/97f5289708b2ad4a.
Report an issue: GitHub.