{"record":{"id":"be5c9d95d7bdc001","repo":"hashicorp/nomad","slug":"no-node-events-given","errorCode":null,"errorMessage":"no node events given","messagePattern":"no node events given","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/node_endpoint.go","lineNumber":1981,"sourceCode":"\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\treturn evalIDs, evalIndex, nil\n}\n\nfunc (n *Node) EmitEvents(args *structs.EmitNodeEventsRequest, reply *structs.EmitNodeEventsResponse) error {\n\taclObj, err := n.srv.AuthenticateClientOnly(n.ctx, args)\n\tif done, err := n.srv.forward(\"Node.EmitEvents\", args, args, reply); done {\n\t\treturn err\n\t}\n\tn.srv.MeasureRPCRate(\"node\", structs.RateMetricWrite, args)\n\tif err != nil {\n\t\treturn structs.ErrPermissionDenied\n\t}\n\tdefer metrics.MeasureSince([]string{\"nomad\", \"client\", \"emit_events\"}, time.Now())\n\n\tif len(args.NodeEvents) == 0 {\n\t\treturn fmt.Errorf(\"no node events given\")\n\t}\n\n\tcallerNodeID := auth.AuthenticatedNodeID(args.GetIdentity())\n\tif callerNodeID == \"\" {\n\t\treturn structs.ErrPermissionDenied\n\t}\n\n\tfor nodeID, events := range args.NodeEvents {\n\t\tif len(events) == 0 {\n\t\t\treturn fmt.Errorf(\"no node events given for node %q\", nodeID)\n\t\t}\n\n\t\tnode, err := n.srv.State().NodeByID(nil, nodeID)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif node == nil || !aclObj.AllowClientOp(node.NodePool) {\n\t\t\treturn structs.ErrPermissionDenied","sourceCodeStart":1963,"sourceCodeEnd":1999,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/node_endpoint.go#L1963-L1999","documentation":"The client EmitEvents RPC requires at least one node event in the request. Node.EmitEvents returns this error immediately when args.NodeEvents is empty. It is a guard against no-op event-emission RPCs from clients.","triggerScenarios":"A Nomad client (or any caller authenticated as a node) sends Node.EmitEvents with an empty or nil NodeEvents map.","commonSituations":"Client plugin or driver code that batch-accumulates events but flushes an empty batch (e.g. a ticker firing with no events collected); custom tooling calling the emit_events endpoint directly.","solutions":["Skip the RPC call when the batch of events is empty (check len(events) == 0 before calling)","Fix the batching logic so a flush only happens when at least one event was collected","If writing a driver/plugin, ensure event delivery only invokes the event hook with real events"],"exampleFix":"// before\nsrv.Node().EmitEvents(events) // events may be empty\n// after\nif len(events) > 0 {\n    srv.Node().EmitEvents(events)\n}","handlingStrategy":"validation","validationCode":"if len(args.NodeEvents) == 0 {\n    return nil // skip EmitEvents call entirely\n}","typeGuard":"func hasNodeEvents(events map[string][]*structs.NodeEvent) bool {\n    for _, evs := range events {\n        if len(evs) > 0 { return true }\n    }\n    return false\n}","tryCatchPattern":"err := client.Nodes().EmitEvents(args)\nif err != nil && strings.Contains(err.Error(), \"no node events given\") {\n    // treat as benign: drop the empty batch\n}","preventionTips":["Only flush event batches when non-empty","Sanitize event maps before RPC","Avoid tick-based flushers that fire on empty buffers","Unit-test event batching code with empty inputs"],"tags":["nomad","rpc","events","validation"],"backgroundTag":"empty-payload-rejected","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}