{"record":{"id":"5008463b88cd1808","repo":"hashicorp/nomad","slug":"missing-external-node-id-v","errorCode":null,"errorMessage":"missing external node ID: %v","messagePattern":"missing external node ID: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/csi_endpoint.go","lineNumber":569,"sourceCode":"\tif plug == nil || !plug.HasControllerCapability(structs.CSIControllerSupportsAttachDetach) {\n\t\treturn nil\n\t}\n\n\t// get Nomad's ID for the client node (not the storage provider's ID)\n\ttargetNode, err := state.NodeByID(ws, alloc.NodeID)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif targetNode == nil {\n\t\treturn fmt.Errorf(\"%w %s\", structs.ErrUnknownNode, alloc.NodeID)\n\t}\n\n\t// if the RPC is sent by a client node, it may not know the claim's\n\t// external node ID.\n\tif req.ExternalNodeID == \"\" {\n\t\texternalNodeID, err := v.lookupExternalNodeID(vol, req.ToClaim())\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"missing external node ID: %v\", err)\n\t\t}\n\t\treq.ExternalNodeID = externalNodeID\n\t}\n\n\tmethod := \"ClientCSI.ControllerAttachVolume\"\n\tcReq := &cstructs.ClientCSIControllerAttachVolumeRequest{\n\t\tVolumeID:        vol.RemoteID(),\n\t\tClientCSINodeID: req.ExternalNodeID,\n\t\tAttachmentMode:  req.AttachmentMode,\n\t\tAccessMode:      req.AccessMode,\n\t\tMountOptions:    csiVolumeMountOptions(vol.MountOptions),\n\t\tReadOnly:        req.Claim == structs.CSIVolumeClaimRead,\n\t\tSecrets:         vol.Secrets,\n\t\tVolumeContext:   vol.Context,\n\t}\n\tcReq.PluginID = plug.ID\n\tcResp := &cstructs.ClientCSIControllerAttachVolumeResponse{}\n","sourceCodeStart":551,"sourceCodeEnd":587,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/csi_endpoint.go#L551-L587","documentation":"controllerPublishVolume could not determine the CSI external node ID — the storage provider's identifier for the client node — when the claim request did not supply one. It wraps the underlying lookupExternalNodeID error with the \"missing external node ID: \" prefix. Without this ID the controller plugin cannot be told which node to attach the volume to.","triggerScenarios":"Raised when req.ExternalNodeID is empty in the CSIVolumeClaimRequest and v.lookupExternalNodeID(vol, req.ToClaim()) fails. lookupExternalNodeID scans the volume's existing PublishClaims for a claim on the allocation's node; it fails when no published claim exists for that node (e.g. first-time publish from a node-only claim path where the node plugin has not yet reported its external ID, or the past claim was already released).","commonSituations":"A client node's CSI NodeInfo was never registered (plugin task not running or not healthy), so no external node ID was recorded on the volume. Also seen when claims are created in an unexpected order (controller publish attempted before node registration completed), or after state loss on the volume's claim list.","solutions":["Verify the CSI plugin (node task) is running and healthy on the target client: `nomad plugin status <plugin-id>`; the node must have registered its CSI NodeInfo.","Ensure the volume was staged/published on the node first (node claims populated) before the controller publish path needs the external node ID.","Retry after the client re-registers the plugin — Nomad will fill the external node ID from the node's CSI info.","If claims were lost, release and re-create the volume claim so Nomad rebuilds the publish state."],"exampleFix":"// before: claiming immediately after node start before plugin registers\nclient.CSIVolumes().Claim(...)\n// after: poll plugin status until node info exists\nfor i := 0; i < 30; i++ {\n    p, _, _ := client.CSIPlugins().Info(pluginID, nil)\n    if p != nil && len(p.Nodes.HealthyNodes) > 0 { break }\n    time.Sleep(2 * time.Second)\n}\nclient.CSIVolumes().Claim(...)","handlingStrategy":"validation","validationCode":"plug, _, err := client.CSIPlugins().Info(pluginID, nil)\nif err != nil || plug == nil || plug.Nodes.HealthyNodes == 0 {\n    return fmt.Errorf(\"CSI plugin %s has no healthy node registration; external node ID unavailable\", pluginID)\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"missing external node ID\") {\n    // wait for node plugin registration, then retry claim\n}","preventionTips":["Ensure the CSI node plugin task is running and healthy before scheduling workloads that claim volumes.","Order operations: node plugin registers -> node claim/stage -> controller publish.","Avoid `nomad system gc` while plugins are temporarily unhealthy."],"tags":["nomad","csi","plugin","node-registration","storage"],"backgroundTag":"missing-csi-external-node-id","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"}