{"record":{"id":"53efd8a8b5191aa4","repo":"hashicorp/nomad","slug":"missing-nodeid-53efd8","errorCode":null,"errorMessage":"missing NodeID","messagePattern":"missing NodeID","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/client_rpc.go","lineNumber":214,"sourceCode":"\t\tif err := rpcErr.ErrorOrNil(); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treturn nil, structs.ErrNoNodeConn\n\t}\n\n\treturn mostRecentServer, nil\n}\n\n// forwardClientRPC forwards the RPC specified by method to the node specified\n// by nodeID. Must be done after region forwarding, metrics, and permissions\n// checks.\n//\n// This is a wrapper method for getNodeForRpc, getNodeConn, etc that Client\n// RPCs which only need Servers to forward requests can use.\nfunc (s *Server) forwardClientRPC(method, nodeID string, args, reply any) error {\n\tif nodeID == \"\" {\n\t\treturn errors.New(\"missing NodeID\")\n\t}\n\n\t// Check if the node even exists and is compatible with NodeRpc\n\tsnap, err := s.State().Snapshot()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Make sure Node is new enough to support RPC\n\t_, err = getNodeForRpc(snap, nodeID)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// Get the connection to the client\n\tstate, ok := s.getNodeConn(nodeID)\n\tif !ok {\n\t\t// Make the RPC via another server","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/client_rpc.go#L196-L232","documentation":"Server.forwardClientRPC in nomad/client_rpc.go is the shared wrapper for RPCs that servers forward to Nomad client agents (used by Get, Renew, Apply, Read, Stats — e.g. CSI plugin RPCs). It rejects the call with \"missing NodeID\" when the nodeID argument is the empty string, before checking node existence or establishing a connection to the client agent.","triggerScenarios":"Any caller invoking s.forwardClientRPC(method, \"\", args, reply) — e.g. CSI controller RPCs where the plugin's node ID was not resolved or the request carried an empty NodeID/PluginID-derived node.","commonSituations":"CSI plugin workflows where the volume/request references a node that was never assigned, so NodeID resolution yielded empty; plugin registration issues leaving node IDs blank; custom RPC tooling calling forwardClientRPC directly.","solutions":["Ensure the upstream request (e.g. CSI volume claim) carries a valid NodeID before it reaches the server RPC handler.","Resolve the node for the plugin/volume via the state store before calling forwardClientRPC.","Check nodeID non-empty at the RPC handler entry point and return a clearer error to the client.","Verify the CSI plugin is registered and node-attached (nomad plugin status) to avoid empty node IDs."],"exampleFix":"// before\nerr := s.forwardClientRPC(method, pluginNodeID, args, reply)\n// after\nif pluginNodeID == \"\" {\n    return structs.NewErrRPCCoded(400, \"node ID not resolved for plugin\")\n}\nerr := s.forwardClientRPC(method, pluginNodeID, args, reply)","handlingStrategy":"validation","validationCode":"if nodeID == \"\" {\n    return fmt.Errorf(\"cannot forward client RPC: node ID is empty\")\n}\nreturn s.forwardClientRPC(method, nodeID, args, reply)","typeGuard":"func forwardable(nodeID string) bool {\n    return nodeID != \"\"\n}","tryCatchPattern":null,"preventionTips":["Resolve the target node (via state store lookup) before invoking forwardClientRPC.","For CSI RPCs, ensure the volume/request carries a valid node identifier from plugin/volume registration.","Add entry-point validation in RPC handlers so empty node IDs produce clearer errors.","Check `nomad node status` / plugin status to confirm the node is registered and eligible."],"tags":["nomad","rpc","forwarding","argument-validation","node"],"backgroundTag":"missing-required-argument","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"}